]> git.donarmstrong.com Git - perltidy.git/commitdiff
New upstream version 20220217 upstream/20220217
authorDon Armstrong <don@donarmstrong.com>
Tue, 24 May 2022 22:27:10 +0000 (15:27 -0700)
committerDon Armstrong <don@donarmstrong.com>
Tue, 24 May 2022 22:27:10 +0000 (15:27 -0700)
46 files changed:
BUGS.md
CHANGES.md
MANIFEST
META.json
META.yml
README.md
bin/perltidy
docs/BugLog.html
docs/ChangeLog.html
docs/Tidy.html
docs/perltidy.html
examples/fix-scbb-csc-bug.pl [deleted file]
lib/Perl/Tidy.pm
lib/Perl/Tidy.pod
lib/Perl/Tidy/Debugger.pm
lib/Perl/Tidy/DevNull.pm
lib/Perl/Tidy/Diagnostics.pm
lib/Perl/Tidy/FileWriter.pm
lib/Perl/Tidy/Formatter.pm
lib/Perl/Tidy/HtmlWriter.pm
lib/Perl/Tidy/IOScalar.pm
lib/Perl/Tidy/IOScalarArray.pm
lib/Perl/Tidy/IndentationItem.pm
lib/Perl/Tidy/LineBuffer.pm
lib/Perl/Tidy/LineSink.pm
lib/Perl/Tidy/LineSource.pm
lib/Perl/Tidy/Logger.pm
lib/Perl/Tidy/Tokenizer.pm
lib/Perl/Tidy/VerticalAligner.pm
lib/Perl/Tidy/VerticalAligner/Alignment.pm
lib/Perl/Tidy/VerticalAligner/Line.pm
pm2pl
t/.gitattributes [new file with mode: 0644]
t/snippets17.t
t/snippets18.t
t/snippets19.t
t/snippets20.t
t/snippets22.t
t/snippets24.t
t/snippets25.t [new file with mode: 0644]
t/snippets26.t [new file with mode: 0644]
t/testwide-passthrough.pl.src [new file with mode: 0644]
t/testwide-passthrough.t.SKIP [new file with mode: 0644]
t/testwide-tidy.pl.src [new file with mode: 0644]
t/testwide-tidy.pl.srctdy [new file with mode: 0644]
t/testwide-tidy.t.SKIP [new file with mode: 0644]

diff --git a/BUGS.md b/BUGS.md
index a64b8ae11edc011eed778c5a2b1b7fa7cda26066..c88aa6e478be471e4c57a7ff8b4c6befe2400636 100644 (file)
--- a/BUGS.md
+++ b/BUGS.md
@@ -29,15 +29,6 @@ it part of the standard Perl distribution.  But for example the following line
 
 which uses double brackets to contain single brackets does not render correctly.
 
-## Two iterations are sometimes needed
-
-Usually the code produced by perltidy on the first pass does not change if it
-is run again, but sometimes a second pass will produce some small additional
-change.  This mainly happens if a major style change is made, particularly when
-perltidy is untangling complex ternary statements. Use the iteration parameter
-**-it=2** if it is important that the results be unchanged on subsequent passes,
-but note that this doubles the run time.
-
 ## Perltidy does not look for here-document targets inside of quoted strings
 
 For example, consider the following script
index 8c36f5997a42b2fd8612550b0b66727147d5ed4b..41e92d60b655f3dd0fa29623a937630566c09fe7 100644 (file)
@@ -1,5 +1,187 @@
 # Perltidy Change Log
 
+## 2022 02 17
+
+    - A new flag, --encode-output-strings, or -eos, has been added to resolve
+      issue git #83. This issue involves the interface between Perl::Tidy and
+      calling programs, and Code::TidyAll (tidyall) in particular.  The problem
+      is that perltidy by default returns decoded character strings, but
+      tidyall expects encoded strings.  This flag provides a fix for that.
+
+      So, tidyall users who process encoded (utf8) files should update to this
+      version of Perl::Tidy and use -eos for tidyall.  For further info see:
+
+      https://github.com/houseabsolute/perl-code-tidyall/issues/84, and
+      https://github.com/perltidy/perltidy/issues/83
+
+      If there are other applications having utf8 problems at the interface
+      with Perl::Tidy, this flag probably may need to be set.
+
+    - The default value of the new flag, --encode-output-strings, -eos, is currently
+      -neos BUT THIS MAY CHANGE in a future release because the current
+      default is inconvenient.  So authors of programs which receive character
+      strings back from Perl::Tidy should set this flag, if necessary,
+      to avoid any problems when the default changes.  For more information see the
+      above links and the Perl::Tidy man pages for example coding.
+
+    - The possible values of the string 's' for the flag '--character-encoding=s'
+      have been limited to 'utf8' (or UTF-8), 'none', or 'guess'.  Previously an
+      arbitrary encoding could also be specified, but as a result of discussions
+      regarding git #83 it became clear that this could cause trouble
+      since the output encoding was still restricted to UTF-8. Users
+      who need to work in other encodings can write a short program calling
+      Perl::Tidy with pre- and post-processing to handle encoding/decoding.
+
+    - A new flag --break-after-labels=i, or -bal=i, was added for git #86.  This
+      controls line breaks after labels, to provide a uniform style, as follows:
+
+            -bal=0 follows the input line breaks [DEFAULT]
+            -bal=1 always break after a label
+            -bal=2 never break after a label
+
+      For example:
+
+          # perltidy -bal=1
+          INIT:
+            {
+                $xx = 1.234;
+            }
+
+          # perltidy -bal=2
+          INIT: {
+                $xx = 1.234;
+            }
+
+    - Fix issue git #82, an error handling something like ${bareword} in a
+      possible indirect object location. Perl allows this, now perltidy does too.
+
+    - The flags -kbb=s or --keep-old-breakpoints-before=s, and its counterpart
+      -kba=s or --keep-old-breakpoints-after=s have expanded functionality
+      for the container tokens: { [ ( } ] ).  The updated man pages have
+      details.
+
+    - Two new flags have been added to provide finer vertical alignment control,
+      --valign-exclusion-list=s (-vxl=s) and  --valign-inclusion-list=s (-vil=s).
+      This has been requested several times, most recently in git #79, and it
+      finally got done.  For example, -vil='=>' means just align on '=>'.
+
+    - A new flag -gal=s, --grep-alias-list=s, has been added as suggested in
+      git #77.  This allows code blocks passed to list operator functions to
+      be formatted in the same way as a code block passed to grep, map, or sort.
+      By default, the following list operators in List::Util are included:
+
+        all any first none notall reduce reductions
+
+      They can be changed with the flag -gaxl=s, -grep-alias-exclusion-list=s
+
+    - A new flag -xlp has been added which can be set to avoid most of the
+      limitations of the -lp flag regarding side comments, blank lines, and
+      code blocks.  See the man pages for more info. This fixes git #64 and git #74.
+      The older -lp flag still works.
+
+    - A new flag -lpil=s, --line-up-parentheses-inclusion-list=s, has been added
+      as an alternative to -lpxl=s, --line-up-parentheses-exclusion-list=s.
+      It supplies equivalent information but is much easier to describe and use.
+      It works for both the older -lp version and the newer -xlp.
+
+    - The coding for the older -lp flag has been updated to avoid some problems
+      and limitations.  The new coding allows the -lp indentation style to
+      mix smoothly with the standard indentation in a single file.  Some problems
+      where -lp and -xci flags were not working well together have been fixed, such
+      as happened in issue rt140025.  As a result of these updates some minor
+      changes in existing code using the -lp style may occur.
+
+    - This version of perltidy was stress-tested for many cpu hours with
+      random input parameters. No failures to converge, internal fault checks,
+      undefined variable references or other irregularities were seen.
+
+    - Numerous minor fixes have been made, mostly very rare formatting
+      instabilities found in random testing.
+
+## 2021 10 29
+
+    - No significant bugs have been found since the last release, but several
+      minor issues have been fixed.  Vertical alignment has been improved for
+      lists of call args which are not contained within parens (next item).
+
+    - Vertical alignment of function calls without parens has been improved with
+      the goal of making vertical alignment essentially the same with or
+      without parens around the call args.  Some examples:
+
+        # OLD
+        mkTextConfig $c, $x, $y, -anchor => 'se', $color;
+        mkTextConfig $c, $x + 30, $y, -anchor => 's',  $color;
+        mkTextConfig $c, $x + 60, $y, -anchor => 'sw', $color;
+        mkTextConfig $c, $x, $y + 30, -anchor => 'e', $color;
+
+        # NEW
+        mkTextConfig $c, $x,      $y,      -anchor => 'se', $color;
+        mkTextConfig $c, $x + 30, $y,      -anchor => 's',  $color;
+        mkTextConfig $c, $x + 60, $y,      -anchor => 'sw', $color;
+        mkTextConfig $c, $x,      $y + 30, -anchor => 'e',  $color;
+
+        # OLD
+        is id_2obj($id), undef, "unregistered object not retrieved";
+        is scalar keys %$ob_reg, 0, "object registry empty";
+        is register($obj), $obj, "object returned by register";
+        is scalar keys %$ob_reg, 1, "object registry nonempty";
+        is id_2obj($id), $obj, "registered object retrieved";
+
+        # NEW
+        is id_2obj($id),         undef, "unregistered object not retrieved";
+        is scalar keys %$ob_reg, 0,     "object registry empty";
+        is register($obj),       $obj,  "object returned by register";
+        is scalar keys %$ob_reg, 1,     "object registry nonempty";
+        is id_2obj($id),         $obj,  "registered object retrieved";
+
+      This will cause some changes in alignment, hopefully for the better,
+      particularly in test code which often uses numerous parenless function
+      calls with functions like 'ok', 'is', 'is_deeply', ....
+
+    - Two new parameters were added to control the block types to which the
+      -bl (--opening-brace-on-new-line) flag applies.  The new parameters are
+      -block-left-list=s, or -bll=s, and --block-left-exclusion-list=s,
+      or -blxl=s.  Previously the -bl flag was 'hardwired' to apply to
+      nearly all blocks. The default values of the new parameters
+      retain the the old default behavior but allow it to be changed.
+
+    - The default behavior of the -bli (-brace-left-and-indent) flag has changed
+      slightly.  Previously, if you set -bli, then the -bl flag would also
+      automatically be set.  Consequently, block types which were not included
+      in the default list for -bli would get -bl formatting.  This is no longer done,
+      and these two styles are now controlled independently.  The manual describes
+      the controls.  If you want to recover the exact previous default behavior of
+      the -bli then add the -bl flag.
+
+    - A partial fix was made for issue for git #74. The -lp formatting style was
+      being lost when a one-line anonymous sub was followed by a closing brace.
+
+    - Fixed issue git #73, in which the -nfpva flag was not working correctly.
+      Some unwanted vertical alignments of spaced function perens
+      were being made.
+
+    - Updated the man pages to clarify the flags -valign and -novalign
+      for turning vertical alignment on and off (issue git #72).
+      Added parameters -vc -vsc -vbc for separately turning off vertical
+      alignment of code, side comments and block comments.
+
+    - Fixed issue git #68, where a blank line following a closing code-skipping
+      comment, '#>>V', could be lost.
+
+    - This version runs 10 to 15 percent faster on large files than the
+      previous release due to optimizations made with the help of NYTProf.
+
+    - This version of perltidy was stress-tested for many cpu hours with
+      random input parameters. No instabilities,  internal fault checks,
+      undefined variable references or other irregularities were seen.
+
+    - Numerous minor fixes have been made, mostly very rare formatting instabilities
+      found in random testing. An effort has been made to minimize changes to
+      existing formatting that these fixes produce, but occasional changes
+      may occur. Many of these updates are listed at:
+
+           https://github.com/perltidy/perltidy/blob/master/local-docs/BugLog.pod
+
 ## 2021 07 17
 
     - This release is being made mainly because of the next item, in which an
       comment '#<<V' which is not terminated with a closing comment '#>>V'. This
       makes code-skipping and format-skipping behave in a similar way: an
       opening comment without a corresponding closing comment will cause
-      the rest of a file to be skipped.  If there is a question about which lines 
-      are skipped, a .LOG file can be produced with the -g flag and it will have 
+      the rest of a file to be skipped.  If there is a question about which lines
+      are skipped, a .LOG file can be produced with the -g flag and it will have
       this information.
 
     - Removed the limit on -ci=n when -xci is set, reference: rt #136415.
     flags and the --line-up-parens flag.
 
     - Fixed issue git #54 regarding irregular application of the --break-before-paren
-    and similar --break-before-xxx flags, in which lists without commas were not 
+    and similar --break-before-xxx flags, in which lists without commas were not
     being formatted according to these flags.
 
-    - Fixed issue git #53. A flag was added to turn off alignment of spaced function 
+    - Fixed issue git #53. A flag was added to turn off alignment of spaced function
     parens.  If the --space-function-paren, -sfp flag is set, a side-effect is that the
     spaced function parens may get vertically aligned.  This can be undesirable,
     so a new parameter '--function-paren-vertical-alignment', or '-fpva', has been
-    added to turn this vertical alignment off. The default is '-fpva', so that 
+    added to turn this vertical alignment off. The default is '-fpva', so that
     existing formatting is not changed.  Use '-nfpva' to turn off unwanted
     vertical alignment.  To illustrate the possibilities:
 
         # perltidy -sfp
         myfun     ( $aaa, $b, $cc );
         mylongfun ( $a, $b, $c );
-    
+
         # perltidy -sfp -nfpva
         myfun ( $aaa, $b, $cc );
         mylongfun ( $a, $b, $c );
 
 ## 2020 12 01
 
-    - This release is being made primarily to make available a several new formatting 
-      parameters, in particular -xci, -kbb=s, -kba=s, and -wnxl=s. No significant 
-      bugs have been found since the previous release, but numerous minor issues have 
+    - This release is being made primarily to make available a several new formatting
+      parameters, in particular -xci, -kbb=s, -kba=s, and -wnxl=s. No significant
+      bugs have been found since the previous release, but numerous minor issues have
       been found and fixed as listed below.
 
     - This version is about 20% faster than the previous version due to optimizations
 
     - Fixed issue git #45, -vtc=n flag was ignored when -wn was set.
 
-    - implement request RT #133649, delete-old-newlines selectively. Two parameters, 
+    - implement request RT #133649, delete-old-newlines selectively. Two parameters,
 
       -kbb=s or --keep-old-breakpoints-before=s, and
       -kba=s or --keep-old-breakpoints-after=s
       were added to request that old breakpoints be kept before or after
       selected token types.  For example, -kbb='=>' means that newlines before
       fat commas should be kept.
-      
     - Fix git #44, fix exit status for assert-tidy/untidy.  The exit status was
       always 0 for --assert-tidy if the user had turned off all error messages with
       the -quiet flag.  This has been fixed.
index 454c8cd1736db0761a66e6be9e18593e76b85e40..e502f89e07475a6911d6d13644dc4d4e40a5a407 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -19,7 +19,6 @@ examples/ex_mp.pl
 examples/filter_example.in
 examples/filter_example.pl
 examples/find_naughty.pl
-examples/fix-scbb-csc-bug.pl
 examples/lextest
 examples/perlcomment.pl
 examples/perllinetype.pl
@@ -56,6 +55,7 @@ Makefile.PL
 MANIFEST                       This list of files
 pm2pl
 README.md
+t/.gitattributes
 t/atee.t
 t/filter_example.t
 t/snippets1.t
@@ -75,6 +75,8 @@ t/snippets21.t
 t/snippets22.t
 t/snippets23.t
 t/snippets24.t
+t/snippets25.t
+t/snippets26.t
 t/snippets3.t
 t/snippets4.t
 t/snippets5.t
@@ -86,6 +88,11 @@ t/test-eol.t
 t/test.t
 t/testsa.t
 t/testss.t
+t/testwide-passthrough.pl.src
+t/testwide-passthrough.t.SKIP
+t/testwide-tidy.pl.src
+t/testwide-tidy.pl.srctdy
+t/testwide-tidy.t.SKIP
 t/testwide.pl.src
 t/testwide.t
 META.yml                                 Module YAML meta-data (added by MakeMaker)
index 2478d27e3a441a648b49416c63981ef93b084409..d289520221f99ac008b1e14a45b0d6a45bac6007 100644 (file)
--- a/META.json
+++ b/META.json
@@ -39,6 +39,6 @@
          "web" : "https://github.com/perltidy/perltidy"
       }
    },
-   "version" : "20210717",
+   "version" : "20220217",
    "x_serialization_backend" : "JSON::PP version 4.04"
 }
index 9f9ba2da4d024f52d67cc7bbd22c38527edf9b3d..07718a43539e31c1e622724e40ea051b061b85b0 100644 (file)
--- a/META.yml
+++ b/META.yml
@@ -19,5 +19,5 @@ no_index:
     - inc
 resources:
   repository: https://github.com/perltidy/perltidy.git
-version: '20210717'
+version: '20220217'
 x_serialization_backend: 'CPAN::Meta::YAML version 0.012'
index bc6a23e003c63eb16877dd71bc9d6c9459ea6592..421bc63a9b5c1c90832cfb7fd8a341f7678f8b91 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,4 +1,8 @@
-[![Build Status](https://travis-ci.com/perltidy/perltidy.svg?branch=master)](https://travis-ci.com/perltidy/perltidy)
+# Build Status
+
++ [![Github Actions Build Status](https://github.com/perltidy/perltidy/actions/workflows/perltest.yml/badge.svg)](https://github.com/perltidy/perltidy/actions)
+* [![Travis-CI Build Status](https://travis-ci.com/perltidy/perltidy.svg?branch=master)](https://travis-ci.com/perltidy/perltidy)
+* [CPAN Testers](https://www.cpantesters.org/distro/P/Perl-Tidy.html)
 
 # Welcome to Perltidy
 
index 34c6b5103ccc58d3a4dc2ae2114337408a646b5f..a24aace57c31444087e392ab00baf170791931cb 100755 (executable)
@@ -37,8 +37,8 @@ perltidy - a perl script indenter and reformatter
 
 Perltidy reads a perl script and writes an indented, reformatted script.
 
-Many users will find enough information in L<"EXAMPLES"> to get 
-started.  New users may benefit from the short tutorial 
+Many users will find enough information in L<"EXAMPLES"> to get
+started.  New users may benefit from the short tutorial
 which can be found at
 http://perltidy.sourceforge.net/tutorial.html
 
@@ -51,17 +51,17 @@ existence of an B<-html> flag.  Without this flag, the output is passed
 through a formatter.  The default formatting tries to follow the
 recommendations in perlstyle(1), but it can be controlled in detail with
 numerous input parameters, which are described in L<"FORMATTING
-OPTIONS">.  
+OPTIONS">.
 
 When the B<-html> flag is given, the output is passed through an HTML
-formatter which is described in L<"HTML OPTIONS">.  
+formatter which is described in L<"HTML OPTIONS">.
 
 =head1 EXAMPLES
 
   perltidy somefile.pl
 
 This will produce a file F<somefile.pl.tdy> containing the script reformatted
-using the default options, which approximate the style suggested in 
+using the default options, which approximate the style suggested in
 perlstyle(1).  The source file F<somefile.pl> is unchanged.
 
   perltidy *.pl
@@ -91,7 +91,7 @@ GNU Coding Standards for C programs.  The output will be F<somefile.pl.tdy>.
 Execute perltidy on file F<somefile.pl>, with 3 columns for each level of
 indentation (B<-i=3>) instead of the default 4 columns.  There will not be any
 tabs in the reformatted script, except for any which already exist in comments,
-pod documents, quotes, and here documents.  Output will be F<somefile.pl.tdy>. 
+pod documents, quotes, and here documents.  Output will be F<somefile.pl.tdy>.
 
   perltidy -i=3 -et=8 somefile.pl
 
@@ -102,7 +102,7 @@ be entabbed with one tab character per 8 spaces.
 
 Execute perltidy on file F<somefile.pl> with all defaults except use "cuddled
 elses" (B<-ce>) and a maximum line length of 72 columns (B<-l=72>) instead of
-the default 80 columns.  
+the default 80 columns.
 
   perltidy -g somefile.pl
 
@@ -127,7 +127,7 @@ it will be created.  If it exists, it will not be overwritten.
 
 Write an html snippet with only the PRE section to F<somefile.pl.html>.
 This is useful when code snippets are being formatted for inclusion in a
-larger web page.  No style sheet will be written in this case.  
+larger web page.  No style sheet will be written in this case.
 
   perltidy -html -ss >mystyle.css
 
@@ -145,7 +145,7 @@ The entire command line is scanned for options, and they are processed
 before any files are processed.  As a result, it does not matter
 whether flags are before or after any filenames.  However, the relative
 order of parameters is important, with later parameters overriding the
-values of earlier parameters.  
+values of earlier parameters.
 
 For each parameter, there is a long name and a short name.  The short
 names are convenient for keyboard input, while the long names are
@@ -171,11 +171,11 @@ The following parameters concern the files which are read and written.
 
 =over 4
 
-=item B<-h>,    B<--help> 
+=item B<-h>,    B<--help>
 
 Show summary of usage and exit.
 
-=item  B<-o>=filename,    B<--outfile>=filename  
+=item  B<-o>=filename,    B<--outfile>=filename
 
 Name of the output file (only if a single input file is being
 processed).  If no output file is specified, and output is not
@@ -193,7 +193,7 @@ request outputting to the standard output.  For example,
 
   perltidy somefile.pl -st >somefile.new.pl
 
-This option may only be used if there is just a single input file.  
+This option may only be used if there is just a single input file.
 The default is B<-nst> or B<--nostandard-output>.
 
 =item  B<-se>,    B<--standard-error-output>
@@ -205,13 +205,13 @@ output stream instead.  This directive may be negated with B<-nse>.
 Thus, you may place B<-se> in a F<.perltidyrc> and override it when
 desired with B<-nse> on the command line.
 
-=item  B<-oext>=ext,    B<--output-file-extension>=ext  
+=item  B<-oext>=ext,    B<--output-file-extension>=ext
 
 Change the extension of the output file to be F<ext> instead of the
 default F<tdy> (or F<html> in case the -B<-html> option is used).
 See L<Specifying File Extensions>.
 
-=item  B<-opath>=path,    B<--output-path>=path  
+=item  B<-opath>=path,    B<--output-path>=path
 
 When perltidy creates a filename for an output file, by default it merely
 appends an extension to the path and basename of the input file.  This
@@ -237,17 +237,17 @@ or if it is being specified explicitly with the B<-o=s> parameter.
 Modify the input file or files in-place and save the original with the
 extension F<.bak>.  Any existing F<.bak> file will be deleted.  See next
 item for changing the default backup extension, and for eliminating the
-backup file altogether.  
+backup file altogether.
 
 A B<-b> flag will be ignored if input is from standard input or goes to
-standard output, or if the B<-html> flag is set.  
+standard output, or if the B<-html> flag is set.
 
 In particular, if you want to use both the B<-b> flag and the B<-pbp>
 (--perl-best-practices) flag, then you must put a B<-nst> flag after the
 B<-pbp> flag because it contains a B<-st> flag as one of its components,
 which means that output will go to the standard output stream.
 
-=item  B<-bext>=ext,    B<--backup-file-extension>=ext  
+=item  B<-bext>=ext,    B<--backup-file-extension>=ext
 
 This parameter serves two purposes: (1) to change the extension of the backup
 file to be something other than the default F<.bak>, and (2) to indicate
@@ -259,7 +259,7 @@ L<Specifying File Extensions>.
 A backup file of the source is always written, but you can request that it
 be deleted at the end of processing if there were no errors.  This is risky
 unless the source code is being maintained with a source code control
-system.  
+system.
 
 To indicate that the backup should be deleted include one forward slash,
 B</>, in the extension.  If any text remains after the slash is removed
@@ -274,7 +274,7 @@ Here are some examples:
   <-bext='/backup'>   F<.backup>         Delete if no errors
   <-bext='original/'> F<.original>       Delete if no errors
 
-=item B<-w>,    B<--warning-output>             
+=item B<-w>,    B<--warning-output>
 
 Setting B<-w> causes any non-critical warning
 messages to be reported as errors.  These include messages
@@ -282,9 +282,9 @@ about possible pod problems, possibly bad starting indentation level,
 and cautions about indirect object usage.  The default, B<-nw> or
 B<--nowarning-output>, is not to include these warnings.
 
-=item B<-q>,    B<--quiet>             
+=item B<-q>,    B<--quiet>
 
-Deactivate error messages (for running under an editor). 
+Deactivate error messages (for running under an editor).
 
 For example, if you use a vi-style editor, such as vim, you may execute
 perltidy as a filter from within the editor using something like
@@ -295,7 +295,7 @@ where C<n1,n2> represents the selected text.  Without the B<-q> flag,
 any error message may mess up your screen, so be prepared to use your
 "undo" key.
 
-=item B<-log>,    B<--logfile>           
+=item B<-log>,    B<--logfile>
 
 Save the F<.LOG> file, which has many useful diagnostics.  Perltidy always
 creates a F<.LOG> file, but by default it is deleted unless a program bug is
@@ -307,18 +307,18 @@ Set maximum interval between input code lines in the logfile.  This purpose of
 this flag is to assist in debugging nesting errors.  The value of C<n> is
 optional.  If you set the flag B<-g> without the value of C<n>, it will be
 taken to be 1, meaning that every line will be written to the log file.  This
-can be helpful if you are looking for a brace, paren, or bracket nesting error. 
+can be helpful if you are looking for a brace, paren, or bracket nesting error.
 
 Setting B<-g> also causes the logfile to be saved, so it is not necessary to
-also include B<-log>. 
+also include B<-log>.
 
 If no B<-g> flag is given, a value of 50 will be used, meaning that at least
 every 50th line will be recorded in the logfile.  This helps prevent
-excessively long log files.  
+excessively long log files.
 
 Setting a negative value of C<n> is the same as not setting B<-g> at all.
 
-=item B<-npro>  B<--noprofile>    
+=item B<-npro>  B<--noprofile>
 
 Ignore any F<.perltidyrc> command file.  Normally, perltidy looks first in
 your current directory for a F<.perltidyrc> file of parameters.  (The format
@@ -329,7 +329,7 @@ in your home directory.
 
 If you set the B<-npro> flag, perltidy will not look for this file.
 
-=item B<-pro=filename> or  B<--profile=filename>    
+=item B<-pro=filename> or  B<--profile=filename>
 
 To simplify testing and switching .perltidyrc files, this command may be
 used to specify a configuration file which will override the default
@@ -338,7 +338,7 @@ name of .perltidyrc.  There must not be a space on either side of the
 
    perltidy -pro=testcfg
 
-would cause file F<testcfg> to be used instead of the 
+would cause file F<testcfg> to be used instead of the
 default F<.perltidyrc>.
 
 A pathname begins with three dots, e.g. ".../.perltidyrc", indicates that
@@ -346,19 +346,19 @@ the file should be searched for starting in the current directory and
 working upwards. This makes it easier to have multiple projects each with
 their own .perltidyrc in their root directories.
 
-=item B<-opt>,   B<--show-options>      
+=item B<-opt>,   B<--show-options>
 
-Write a list of all options used to the F<.LOG> file.  
+Write a list of all options used to the F<.LOG> file.
 Please see B<--dump-options> for a simpler way to do this.
 
-=item B<-f>,   B<--force-read-binary>      
+=item B<-f>,   B<--force-read-binary>
 
 Force perltidy to process binary files.  To avoid producing excessive
 error messages, perltidy skips files identified by the system as non-text.
 However, valid perl scripts containing binary data may sometimes be identified
 as non-text, and this flag forces perltidy to process them.
 
-=item B<-ast>,   B<--assert-tidy>      
+=item B<-ast>,   B<--assert-tidy>
 
 This flag asserts that the input and output code streams are identical, or in
 other words that the input code is already 'tidy' according to the formatting
@@ -370,26 +370,52 @@ perltidy.  This might be useful for certain code maintenance operations.
 Note: you will not see this message if you have error messages turned off with the
 -quiet flag.
 
-=item B<-asu>,   B<--assert-untidy>      
+=item B<-asu>,   B<--assert-untidy>
 
 This flag asserts that the input and output code streams are different, or in
 other words that the input code is 'untidy' according to the formatting
 parameters.  If this is not the case, an error message noting this is produced.
 This flag has no other effect on the functioning of perltidy.
 
-=item B<-sal=s>,   B<--sub-alias-list=s>      
+=item B<-sal=s>,   B<--sub-alias-list=s>
 
 This flag causes one or more words to be treated the same as if they were the keyword 'sub'.  The string B<s> contains one or more alias words, separated by spaces or commas.
 
 For example,
 
-        perltidy -sal='method fun _sub M4' 
+        perltidy -sal='method fun _sub M4'
 
 will cause the perltidy to treate the words 'method', 'fun', '_sub' and 'M4' to be treated the same as if they were 'sub'.  Note that if the alias words are separated by spaces then the string of words should be placed in quotes.
 
 Note that several other parameters accept a list of keywords, including 'sub' (see L<Specifying Block Types>).
 You do not need to include any sub aliases in these lists. Just include keyword 'sub' if you wish, and all aliases are automatically included.
 
+=item B<-gal=s>,   B<--grep-alias-list=s>
+
+This flag allows a code block following an external 'list operator' function to be formatted as if it followed one of the builtin keywords B<grep>,  B<map> or B<sort>.  The string B<s> contains the names of one or more such list operators, separated by spaces or commas.
+
+By 'list operator' is meant a function which is invoked in the form
+
+      word {BLOCK} @list
+
+Perltidy tries to keep code blocks for these functions intact, since they are usually short, and does not automatically break after the closing brace since a list may follow. It also does some special handling of continuation indentation.
+
+For example, the code block arguments to functions 'My_grep' and 'My_map' can be given formatting like 'grep' with
+
+        perltidy -gal='My_grep My_map'
+
+By default, the following list operators in List::Util are automatically included:
+
+      all any first none notall reduce reductions
+
+Any operators specified with B<--grep-alias-list> are added to this list.
+The next parameter can be used to remove words from this default list.
+
+=item B<-gaxl=s>,   B<--grep-alias-exclusion-list=s>
+
+The B<-gaxl=s> flag provides a method for removing any of the default list operators given above
+by listing them in the string B<s>.  To remove all of the default operators use B<-gaxl='*'>.
+
 =back
 
 =head1 FORMATTING OPTIONS
@@ -407,7 +433,7 @@ set of F<.perltidyrc> files to avoid unwanted code tidying.  See also
 L<Skipping Selected Sections of Code> for a way to avoid tidying specific
 sections of code.
 
-=item B<-i=n>,  B<--indent-columns=n>  
+=item B<-i=n>,  B<--indent-columns=n>
 
 Use n columns per indentation level (default n=4).
 
@@ -415,7 +441,7 @@ Use n columns per indentation level (default n=4).
 
 The default maximum line length is n=80 characters.  Perltidy will try
 to find line break points to keep lines below this length. However, long
-quotes and side comments may cause lines to exceed this length. 
+quotes and side comments may cause lines to exceed this length.
 
 The default length of 80 comes from the past when this was the standard CRT
 screen width.  Many programmers prefer to increase this to something like 120.
@@ -430,9 +456,9 @@ A problem arises using a fixed maximum line length with very deeply nested code
 and data structures because eventually the amount of leading whitespace used
 for indicating indentation takes up most or all of the available line width,
 leaving little or no space for the actual code or data.  One solution is to use
-a vary long line length.  Another solution is to use the B<-vmll> flag, which
+a very long line length.  Another solution is to use the B<-vmll> flag, which
 basically tells perltidy to ignore leading whitespace when measuring the line
-length.  
+length.
 
 To be precise, when the B<-vmll> parameter is set, the maximum line length of a
 line of code will be M+L*I, where
@@ -444,7 +470,7 @@ line of code will be M+L*I, where
 When this flag is set, the choice of breakpoints for a block of code should be
 essentially independent of its nesting depth.  However, the absolute line
 lengths, including leading whitespace, can still be arbitrarily large.  This
-problem can be avoided by including the next parameter.  
+problem can be avoided by including the next parameter.
 
 The default is not to do this (B<-nvmll>).
 
@@ -454,7 +480,7 @@ This flag also addresses problems with very deeply nested code and data
 structures.  When the nesting depth exceeds the value B<n> the leading
 whitespace will be reduced and start at a depth of 1 again.  The result is that
 blocks of code will shift back to the left rather than moving arbitrarily far
-to the right.  This occurs cyclically to any depth.  
+to the right.  This occurs cyclically to any depth.
 
 For example if one level of indentation equals 4 spaces (B<-i=4>, the default),
 and one uses B<-wc=15>, then if the leading whitespace on a line exceeds about
@@ -473,7 +499,7 @@ The default is not to use this, which can also be indicated using B<-wc=0>.
 Using tab characters will almost certainly lead to future portability
 and maintenance problems, so the default and recommendation is not to
 use them.  For those who prefer tabs, however, there are two different
-options.  
+options.
 
 Except for possibly introducing tab indentation characters, as outlined
 below, perltidy does not introduce any tab characters into your file,
@@ -486,13 +512,13 @@ here-documents, they will remain.
 =item B<-et=n>,   B<--entab-leading-whitespace>
 
 This flag causes each B<n> initial space characters to be replaced by
-one tab character.  
+one tab character.
 
 The value of the integer B<n> can be any value but can be coordinated with the
 number of spaces used for intentation. For example, B<-et=4 -ci=4 -i=4> will
 produce one tab for each indentation level and and one for each continuation
 indentation level.  You may want to coordinate the value of B<n> with what your
-display software assumes for the spacing of a tab. 
+display software assumes for the spacing of a tab.
 
 =item B<-t>,   B<--tabs>
 
@@ -509,7 +535,7 @@ If the first line of code passed to perltidy contains leading tabs but no
 tab scheme is specified for the output stream then perltidy must guess how many
 spaces correspond to each leading tab.  This number of spaces B<n>
 corresponding to each leading tab of the input stream may be specified with
-B<-dt=n>.  The default is B<n=8>.  
+B<-dt=n>.  The default is B<n=8>.
 
 This flag has no effect if a tab scheme is specified for the output stream,
 because then the input stream is assumed to use the same tab scheme and
@@ -518,11 +544,11 @@ unstable editing).
 
 =back
 
-=item B<-xs>,   B<--extended-syntax>      
+=item B<-xs>,   B<--extended-syntax>
 
 A problem with formatting Perl code is that some modules can introduce new
 syntax.  This flag allows perltidy to handle certain common extensions
-to the standard syntax without complaint.  
+to the standard syntax without complaint.
 
 For example, without this flag a structure such as the following would generate
 a syntax error and the braces would not be balanced:
@@ -541,7 +567,7 @@ messages when debugging a script.
 
 For another method of handling extended syntax see the section L<Skipping Selected Sections of Code>.
 
-=item B<-io>,   B<--indent-only>       
+=item B<-io>,   B<--indent-only>
 
 This flag is used to deactivate all whitespace and line break changes
 within non-blank lines of code.
@@ -555,10 +581,10 @@ useful when perltidy is merely being used to help find a brace error in
 a large script).
 
 Setting this flag is equivalent to setting B<--freeze-newlines> and
-B<--freeze-whitespace>.  
+B<--freeze-whitespace>.
 
 If you also want to keep your existing blank lines exactly
-as they are, you can add B<--freeze-blank-lines>. 
+as they are, you can add B<--freeze-blank-lines>.
 
 With this option perltidy is still free to modify the indenting (and
 outdenting) of code and comments as it normally would.  If you also want to
@@ -572,53 +598,75 @@ this flag is in effect.
 
 =item B<-enc=s>,  B<--character-encoding=s>
 
-This flag indicates the character encoding, if any, of the input data stream.
+This flag indicates if the input data stream use a character encoding.
 Perltidy does not look for the encoding directives in the soure stream, such
 as B<use utf8>, and instead relies on this flag to determine the encoding.
 (Note that perltidy often works on snippets of code rather than complete files
 so it cannot rely on B<use utf8> directives).
 
-The possible values for B<s> are (1) the name of an encoding recognized by the
-Encode.pm module, (2) B<none> if no encoding is used, or (3) <guess> if
-perltidy should guess.  
+The possible values for B<s> are:
 
-For example, the value B<utf8> causes the stream to be read and written as
-UTF-8.  If the input stream cannot be decoded with a specified encoding then
-processing is not done.  
+ -enc=none if no encoding is used, or
+ -enc=utf8 for encoding in utf8
+ -enc=guess if perltidy should guess between these two possibilities.
 
 The value B<none> causes the stream to be processed without special encoding
 assumptions.  This is appropriate for files which are written in single-byte
-character encodings such as latin-1.  
+character encodings such as latin-1.
+
+The value B<utf8> causes the stream to be read and written as
+UTF-8.  If the input stream cannot be decoded with this encoding then
+processing is not done.
 
 The value B<guess> tells perltidy to guess between either utf8 encoding or no
-encoding (meaning one character per byte).  The guess uses the Encode::Guess
-module and this restricted range of guesses covers the most common cases.
-Testing showed that considering any greater number of encodings as guess
-suspects is too risky.
+encoding (meaning one character per byte).  The B<guess> option uses the
+Encode::Guess module which has been found to be reliable at detecting
+if a file is encoded in utf8 or not.
 
-The current default is B<guess>.  
+The current default is B<guess>.
 
 The abbreviations B<-utf8> or B<-UTF8> are equivalent to B<-enc=utf8>, and the
-abbreviation B<-guess> is equivalent to <-enc=guess>.  So to process a file
+abbreviation B<-guess> is equivalent to B<-enc=guess>.  So to process a file
 named B<file.pl> which is encoded in UTF-8 you can use:
 
    perltidy -utf8 file.pl
 
 or
+
    perltidy -guess file.pl
 
-To process a file in B<euc-jp> you could use
+or simply
+
+   perltidy file.pl
+
+since B<-guess> is the default.
+
+To process files with an encoding other than UTF-8, it would be necessary to
+write a short program which calls the Perl::Tidy module with some pre- and
+post-processing to handle decoding and encoding.
+
+=item B<-eos=s>,   B<--encode-output-strings=s>
 
-   perltidy -enc=euc-jp file.pl
+This flag has been added to resolve an issue involving the interface between
+Perl::Tidy and calling programs, and in particular B<Code::TidyAll (tidyall)>.
+By default Perl::Tidy returns unencoded strings to the calling
+program, but some programs expect encoded strings. Setting this flag causes
+Perl::Tidy to return encoded output strings which it decoded.  For some
+background information see
+L<https://github.com/perltidy/perltidy/issues/83> and
+L<https://github.com/houseabsolute/perl-code-tidyall/issues/84>.
 
-A perltidy output file is unencoded if the input file is unencoded, and
-otherwise it is encoded as B<utf8>, even if the input encoding was not
-B<utf8>.
+If you only run the B<perltidy> binary this flag has no effect.
+
+If you use B<tidyall> with encoded files and encounter irregularities such as
+B<wide character> messages you should set this flag.
+
+Additional information can be found in the man pages for the B<Perl::Tidy> module.
 
 =item B<-gcs>,  B<--use-unicode-gcstring>
 
 This flag controls whether or not perltidy may use module Unicode::GCString to
-obtain accurate display widths of wide characters.  The default 
+obtain accurate display widths of wide characters.  The default
 is B<--nouse-unicode-gcstring>.
 
 If this flag is set, and text is encoded, perltidy will look for the module
@@ -702,19 +750,19 @@ reformatting complex code structures, such as deeply nested ternary statements.
 Continuation indentation is extra indentation spaces applied when
 a long line is broken.  The default is n=2, illustrated here:
 
- my $level =   # -ci=2      
+ my $level =   # -ci=2
    ( $max_index_to_go >= 0 ) ? $levels_to_go[0] : $last_output_level;
 
 The same example, with n=0, is a little harder to read:
 
- my $level =   # -ci=0    
+ my $level =   # -ci=0
  ( $max_index_to_go >= 0 ) ? $levels_to_go[0] : $last_output_level;
 
 The value given to B<-ci> is also used by some commands when a small
 space is required.  Examples are commands for outdenting labels,
-B<-ola>, and control keywords, B<-okw>.  
+B<-ola>, and control keywords, B<-okw>.
 
-When default values are not used, it is recommended that either 
+When default values are not used, it is recommended that either
 
 (1) the value B<n> given with B<-ci=n> be no more than about one-half of the
 number of spaces assigned to a full indentation level on the B<-i=n> command, or
@@ -758,11 +806,11 @@ improve indentation of some multi-line qw lists as shown below.
                 )
               )
 
-=item B<-sil=n> B<--starting-indentation-level=n>   
+=item B<-sil=n> B<--starting-indentation-level=n>
 
 By default, perltidy examines the input file and tries to determine the
 starting indentation level.  While it is often zero, it may not be
-zero for a code snippet being sent from an editing session.  
+zero for a code snippet being sent from an editing session.
 
 To guess the starting indentation level perltidy simply assumes that
 indentation scheme used to create the code snippet is the same as is being used
@@ -777,10 +825,19 @@ guessed indentation will be wrong.
 If the default method does not work correctly, or you want to change the
 starting level, use B<-sil=n>, to force the starting level to be n.
 
-=item B<List indentation> using B<-lp>, B<--line-up-parentheses>
+=item B<List indentation> using B<--line-up-parentheses>, B<-lp> or B<--extended--line-up-parentheses> , B<-xlp>
+
+These flags provide an alternative indentation method for list data.  The
+original flag for this is B<-lp>, but it has some limitations (explained below)
+which are avoided with the newer B<-xlp> flag.  So B<-xlp> is probably the better
+choice for new work, but the B<-lp> flag is retained to minimize changes to
+existing formatting.
+If you enter both B<-lp> and B<-xlp>, then B<-xlp> will be used.
 
-By default, perltidy indents lists with 4 spaces, or whatever value
-is specified with B<-i=n>.  Here is a small list formatted in this way:
+
+In the default indentation method perltidy indents lists with 4 spaces, or
+whatever value is specified with B<-i=n>.  Here is a small list formatted in
+this way:
 
     # perltidy (default)
     @month_of_year = (
@@ -788,84 +845,135 @@ is specified with B<-i=n>.  Here is a small list formatted in this way:
         'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
     );
 
-Use the B<-lp> flag to add extra indentation to cause the data to begin
-past the opening parentheses of a sub call or list, or opening square
-bracket of an anonymous array, or opening curly brace of an anonymous
-hash.  With this option, the above list would become:
+The B<-lp> or B<-xlp> flags add extra indentation to cause the data to begin
+past the opening parentheses of a sub call or list, or opening square bracket
+of an anonymous array, or opening curly brace of an anonymous hash.  With this
+option, the above list would become:
 
-    # perltidy -lp
+    # perltidy -lp or -xlp
     @month_of_year = (
                        'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
                        'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
     );
 
-If the available line length (see B<-l=n> ) does not permit this much 
+If the available line length (see B<-l=n> ) does not permit this much
 space, perltidy will use less.   For alternate placement of the
 closing paren, see the next section.
 
-This option has no effect on code BLOCKS, such as if/then/else blocks,
+These flags have no effect on code BLOCKS, such as if/then/else blocks,
 which always use whatever is specified with B<-i=n>.
 
-In situations where perltidy does not have complete freedom to choose line
-breaks it may temporarily revert to its default indentation method.  This can
-occur for example if there are blank lines, block comments, multi-line quotes,
-or side comments between the opening and closing parens, braces, or brackets.
+Some limitiations on these flags are:
 
-In addition, any parameter which significantly restricts the ability of
-perltidy to choose newlines will conflict with B<-lp> and will cause
-B<-lp> to be deactivated.  These include B<-io>, B<-fnl>, B<-nanl>, and
-B<-ndnl>.  The reason is that the B<-lp> indentation style can require
-the careful coordination of an arbitrary number of break points in
-hierarchical lists, and these flags may prevent that.
+=over 4
 
-The B<-lp> option may not be used together with the B<-t> tabs option.
-It may, however, be used with the B<-et=n> tab method.
+=item *
 
+A limitation on B<-lp>, but not B<-xlp>, occurs in situations where perltidy
+does not have complete freedom to choose line breaks. Then it may temporarily revert
+to its default indentation method.  This can occur for example if there are
+blank lines, block comments, multi-line quotes, or side comments between the
+opening and closing parens, braces, or brackets.  It will also occur if a
+multi-line anonymous sub occurs within a container since that will impose
+specific line breaks (such as line breaks after statements).
 
-=item B<-lpxl=s>,  B<--line-up-parentheses-exclusion-list>
+=item *
 
-This is an experimental parameter; the details might change as experience
-with it is gained.
+For both the B<-lp> and B<-xlp> flags, any parameter which significantly
+restricts the ability of perltidy to choose newlines will conflict with these
+flags and will cause them to be deactivated.  These include B<-io>, B<-fnl>,
+B<-nanl>, and B<-ndnl>.
 
-The B<-lp> indentation style works well for some types of coding but can
-produce very long lines when variables have long names and/or containers are
-very deeply nested.  The B<-lpxl=s> flag is intended to help mitigate this problem by
-providing control over the containers to which the B<-lp> indentation style is
-applied.  The B<-lp> flag by default is "greedy" and applies to as many
-containers as possible.  This flag specifies a list of things which should
-B<not> be use B<-lp> indentation.
+=item *
 
-This list is a string with space-separated items.  Each item consists of up to
-three pieces of information in this order: (1) an optional letter code (2) a
-required container type, and (3) an optional numeric code.
+The B<-lp> and B<-xlp> options may not be used together with the B<-t> tabs option.
+They may, however, be used with the B<-et=n> tab method
 
-The only required piece of information is a container type, which is one of
-'(', '[', or '{'.  For example the string
+=back
 
-  -lpxl='[ {'
+There are some potential disadvantages of this indentation method compared to
+the default method that should be noted:
 
-means do B<NOT> include use -lp formatting within square-bracets or braces.  The only unspecified
-container is '(', so this string means that only the contents within parens will use -lp indentation.
+=over 4
 
-An optional numeric code may follow any of the container types to further refine the selection based
-on container contents.  The numeric codes are:
+=item *
 
-  '0' or blank: no check on contents
-  '1' reject -lp unless the contents is a simple list without sublists
-  '2' reject -lp unless the contents is a simple list without sublists, without
-      code blocks, and without ternary operators
+The available line length can quickly be used up if variable names are
+long.  This can cause deeply nested code to quickly reach the line length
+limit, and become badly formatted, much sooner than would occur with the
+default indentation method.
 
-For example,
+=item *
 
-  -lpxl = '[ { (2'
+Since the indentation depends on the lengths of variable names, small
+changes in variable names can cause changes in indentation over many lines in a
+file.  This means that minor name changes can produce significant file
+differences.  This can be annoying and does not occur with the default
+indentation method.
 
-means only apply -lp to parenthesized lists which do not contain any sublists,
-code blocks or ternary expressions.
+=back
+
+Some things that can be done to minimize these problems are:
+
+=over 4
+
+=item *
+
+Increase B<--maximum-line-length=n> above the default B<n=80> characters if
+necessary.
+
+=item *
+
+If you use B<-xlp> then long side comments can limit the indentation over
+multiple lines.  Consider adding the flag B<--ignore-side-comment-lengths> to
+prevent this, or minimizing the use of side comments.
+
+=item *
+
+Apply this style in a limited way.  By default, it applies to all list
+containers (not just lists in parentheses).  The next section describes how to
+limit this style to, for example, just function calls.  The default indentation
+method will be applied elsewhere.
+
+=back
+
+=item B<-lpil=s>, B<--line-up-parentheses-inclusion-list> and B<-lpxl=s>,  B<--line-up-parentheses-exclusion-list>
+
+The following discussion is written for B<-lp> but applies equally to the newer B<-xlp> version.
+By default, the B<-lp> flag applies to as many containers as possible.
+The set of containers to which the B<-lp> style applies can be reduced by
+either one of these two flags:
+
+Use B<-lpil=s> to specify the containers to which B<-lp> applies, or
 
-A third optional item of information which can be given for parens is an alphanumeric
+use B<-lpxl=s> to specify the containers to which B<-lp> does NOT apply.
+
+Only one of these two flags may be used.  Both flags can achieve the same
+result, but the B<-lpil=s> flag is much easier to describe and use and is
+recommended.  The B<-lpxl=s> flag was the original implementation and is
+only retained for backwards compatibility.
+
+This list B<s> for these parametes is a string with space-separated items.
+Each item consists of up to three pieces of information in this order: (1) an
+optional letter code (2) a required container type, and (3) an optional numeric
+code.
+
+The only required piece of information is a container type, which is one of
+'(', '[', or '{'.  For example the string
+
+  -lpil='('
+
+means use -lp formatting only on lists within parentheses, not lists in square-bracets or braces.
+The same thing could alternatively be specified with
+
+  -lpxl = '[ {'
+
+which says to exclude lists within square-brackets and braces.  So what remains is lists within parentheses.
+
+A second optional item of information which can be given for parentheses is an alphanumeric
 letter which is used to limit the selection further depending on the type of
 token immediately before the paren.  The possible letters are currently 'k',
-'K', 'f', 'F', 'w', and 'W', with these meanings:
+'K', 'f', 'F', 'w', and 'W', with these meanings for matching whatever precedes an opening paren:
 
  'k' matches if the previous nonblank token is a perl builtin keyword (such as 'if', 'while'),
  'K' matches if 'k' does not, meaning that the previous token is not a keyword.
@@ -874,19 +982,41 @@ token immediately before the paren.  The possible letters are currently 'k',
  'w' matches if either 'k' or 'f' match.
  'W' matches if 'w' does not.
 
+For example:
+
+  -lpil = 'f('
+
+means only apply -lp to function calls, and
+
+  -lpil = 'w('
+
+means only apply -lp to parenthesized lists which follow a function or a keyword.
+
+This last example could alternatively be written using the B<-lpxl=s> flag as
+
+  -lpxl = '[ { W('
+
+which says exclude B<-lp> for lists within square-brackets, braces, and parens NOT preceded by
+a keyword or function.   Clearly, the B<-lpil=s> method is easier to understand.
+
+An optional numeric code may follow any of the container types to further refine the selection based
+on container contents.  The numeric codes are:
+
+  '0' or blank: no check on contents is made
+  '1' exclude B<-lp> unless the contents is a simple list without sublists
+  '2' exclude B<-lp> unless the contents is a simple list without sublists, without
+      code blocks, and without ternary operators
+
 For example,
 
-  -lpxl = '[ { F(2'
+  -lpil = 'f(2'
 
-means only apply -lp to parenthesized lists which follow a function call and
-which do not contain any sublists, code blocks or ternary expressions.  The logic
-of writing these codes is somewhat counter-intuitive because they describe what is not
-getting the -lp indentation.  So the 'F' indicates that non-function calls are
-not getting -lp, or in other words that function calls are getting the -lp indentation.
+means only apply -lp to function call lists which do not contain any sublists,
+code blocks or ternary expressions.
 
 =item B<-cti=n>, B<--closing-token-indentation>
 
-The B<-cti=n> flag controls the indentation of a line beginning with 
+The B<-cti=n> flag controls the indentation of a line beginning with
 a C<)>, C<]>, or a non-block C<}>.  Such a line receives:
 
  -cti = 0 no extra indentation (default)
@@ -917,10 +1047,10 @@ B<cti=1> is constrained to be no more than one indentation level.
 
 If desired, this control can be applied independently to each of the
 closing container token types.  In fact, B<-cti=n> is merely an
-abbreviation for B<-cpi=n -csbi=n -cbi=n>, where:  
+abbreviation for B<-cpi=n -csbi=n -cbi=n>, where:
 B<-cpi> or B<--closing-paren-indentation> controls B<)>'s,
-B<-csbi> or B<--closing-square-bracket-indentation> controls B<]>'s, 
-B<-cbi> or B<--closing-brace-indentation> controls non-block B<}>'s. 
+B<-csbi> or B<--closing-square-bracket-indentation> controls B<]>'s,
+B<-cbi> or B<--closing-brace-indentation> controls non-block B<}>'s.
 
 =item B<-icp>, B<--indent-closing-paren>
 
@@ -946,7 +1076,7 @@ The default is not to do this, indicated by B<-nicb>.
 =item B<-nib>, B<--non-indenting-braces>
 
 Normally, lines of code contained within a pair of block braces receive one
-additional level of indentation.  This flag, which is enabled by default, 
+additional level of indentation.  This flag, which is enabled by default,
 causes perltidy to look for
 opening block braces which are followed by a special side comment. This special
 side comment is B<#<<<> by default.  If found, the code between this opening brace and its
@@ -977,7 +1107,7 @@ If your code happens to have some opening braces followed by '#<<<', and you
 don't want this behavior, you can use B<-nnib> to deactivate it.  To make it
 easy to remember, the default string is the same as the string for starting a
 B<format-skipping> section. There is no confusion because in that case it is
-for a block comment rather than a side-comment. 
+for a block comment rather than a side-comment.
 
 The special side comment can be changed with the next parameter.
 
@@ -1023,7 +1153,7 @@ has been set to), if possible.  This is the default.  For example:
             fixit($i);
         }
 
-Use B<-nola> to not outdent labels. 
+Use B<-nola> to not outdent labels.  To control line breaks after labels see L<bal=n, --break-after-labels=n>.
 
 =item B<Outdenting Keywords>
 
@@ -1047,7 +1177,7 @@ For example, using C<perltidy -okw> on the previous example gives:
             fixit($i);
         }
 
-The default is not to do this.  
+The default is not to do this.
 
 =item B<Specifying Outdented Keywords:> B<-okwl=string>,  B<--outdent-keyword-list=string>
 
@@ -1099,17 +1229,17 @@ values, 0, 1, and 2:
 When n is 0, there is always a space to the right of a '(' and to the left
 of a ')'.  For n=2 there is never a space.  For n=1, the default, there
 is a space unless the quantity within the parens is a single token, such
-as an identifier or quoted string.  
+as an identifier or quoted string.
 
 Likewise, the parameter B<-sbt=n> or B<--square-bracket-tightness=n>
 controls the space within square brackets, as illustrated below.
 
  $width = $col[ $j + $k ] - $col[ $j ];  # -sbt=0
  $width = $col[ $j + $k ] - $col[$j];    # -sbt=1 (default)
- $width = $col[$j + $k] - $col[$j];      # -sbt=2 
+ $width = $col[$j + $k] - $col[$j];      # -sbt=2
 
 Curly braces which do not contain code blocks are controlled by
-the parameter B<-bt=n> or B<--brace-tightness=n>. 
+the parameter B<-bt=n> or B<--brace-tightness=n>.
 
  $obj->{ $parsed_sql->{ 'table' }[0] };    # -bt=0
  $obj->{ $parsed_sql->{'table'}[0] };      # -bt=1 (default)
@@ -1117,7 +1247,7 @@ the parameter B<-bt=n> or B<--brace-tightness=n>.
 
 And finally, curly braces which contain blocks of code are controlled by the
 parameter B<-bbt=n> or B<--block-brace-tightness=n> as illustrated in the
-example below.   
+example below.
 
  %bf = map { $_ => -M $_ } grep { /\.deb$/ } dirents '.'; # -bbt=0 (default)
  %bf = map { $_ => -M $_ } grep {/\.deb$/} dirents '.';   # -bbt=1
@@ -1132,9 +1262,9 @@ abbreviation for the combination <-pt=n -sbt=n -bt=n -bbt=n>.
 
 The flag B<-tso> causes certain perl token sequences (secret operators)
 which might be considered to be a single operator to be formatted "tightly"
-(without spaces).  The operators currently modified by this flag are: 
+(without spaces).  The operators currently modified by this flag are:
 
-     0+  +0  ()x!! ~~<>  ,=>   =( )=  
+     0+  +0  ()x!! ~~<>  ,=>   =( )=
 
 For example the sequence B<0 +>,  which converts a string to a number,
 would be formatted without a space: B<0+> when the B<-tso> flag is set.  This
@@ -1161,7 +1291,7 @@ B<-nsfs> or B<--nospace-for-semicolon> to deactivate it.
 
 =item B<-asc>,  B<--add-semicolons>
 
-Setting B<-asc> allows perltidy to add any missing optional semicolon at the end 
+Setting B<-asc> allows perltidy to add any missing optional semicolon at the end
 of a line which is followed by a closing curly brace on the next line.  This
 is the default, and may be deactivated with B<-nasc> or B<--noadd-semicolons>.
 
@@ -1226,12 +1356,12 @@ than fixed rules, because perltidy must try to resolve conflicts that
 arise between them and all of the other rules that it uses.  One
 conflict that can arise is if, between two tokens, the left token wants
 a space and the right one doesn't.  In this case, the token not wanting
-a space takes priority.  
+a space takes priority.
 
 It is necessary to have a list of all token types in order to create
 this type of input.  Such a list can be obtained by the command
 B<--dump-token-types>.  Also try the B<-D> flag on a short snippet of code
-and look at the .DEBUG file to see the tokenization. 
+and look at the .DEBUG file to see the tokenization.
 
 B<WARNING> Be sure to put these tokens in quotes to avoid having them
 misinterpreted by your command shell.
@@ -1269,7 +1399,7 @@ unchanged in formatting.
    system($foo );
    system($foo);
 
-To find if a token is of type B<Z> you can use B<perltidy -DEBUG>. For the 
+To find if a token is of type B<Z> you can use B<perltidy -DEBUG>. For the
 first line above the result is
 
    1: system($foo );
@@ -1316,7 +1446,7 @@ binary operator asymetrically with a space on the left but not on the right.
 When an opening paren follows a Perl keyword, no space is introduced after the
 keyword, unless it is (by default) one of these:
 
-   my local our and or xor eq ne if else elsif until unless 
+   my local our and or xor eq ne if else elsif until unless
    while for foreach return switch case given when
 
 These defaults can be modified with two commands:
@@ -1325,7 +1455,7 @@ B<-sak=s>  or B<--space-after-keyword=s>  adds keywords.
 
 B<-nsak=s>  or B<--nospace-after-keyword=s>  removes keywords.
 
-where B<s> is a list of keywords (in quotes if necessary).  For example, 
+where B<s> is a list of keywords (in quotes if necessary).  For example,
 
   my ( $a, $b, $c ) = @_;    # default
   my( $a, $b, $c ) = @_;     # -nsak="my local our"
@@ -1357,7 +1487,7 @@ is not to introduce a space.  To cause a space to be introduced use:
 
 B<-sfp>  or B<--space-function-paren>
 
-  myfunc( $a, $b, $c );    # default 
+  myfunc( $a, $b, $c );    # default
   myfunc ( $a, $b, $c );   # -sfp
 
 You will probably also want to use the flag B<-skp> (previous item) too.
@@ -1371,7 +1501,7 @@ reformatted with -sfp:
   sub filename { return $0 }
 
 In this particular case the syntax error can be removed if the line order is
-reversed, so that Perl parses 'sub filename' first. 
+reversed, so that Perl parses 'sub filename' first.
 
 =item B<-fpva>  or B<--function-paren-vertical-alignment>
 
@@ -1434,7 +1564,7 @@ The set of keywords to which this parameter applies are by default are:
 
    if elsif unless while until for foreach
 
-These can be changed with the parameter B<-kpitl=s> described in the next section. 
+These can be changed with the parameter B<-kpitl=s> described in the next section.
 
 
 =item B<-kpitl=string> or B<--keyword-paren-inner-tightness=string>
@@ -1454,9 +1584,9 @@ follow the tightness value indicated by the B<-kpit=2> flag.
 In the following example some extra space has been inserted on the second
 line between the two open parens. This extra space is called "logical padding"
 and is intended to help align similar things vertically in some logical
-or ternary expressions.  
+or ternary expressions.
 
-    # perltidy [default formatting] 
+    # perltidy [default formatting]
     $same =
       (      ( $aP eq $bP )
           && ( $aS eq $bS )
@@ -1471,7 +1601,7 @@ a local version of vertical alignment.
 
 Here is an example involving a ternary operator:
 
-    # perltidy [default formatting] 
+    # perltidy [default formatting]
     $bits =
         $top > 0xffff ? 32
       : $top > 0xff   ? 16
@@ -1587,7 +1717,7 @@ whenever possible.  The default, n=0, will not do this.
 =item B<-iscl>,  B<--ignore-side-comment-lengths>
 
 This parameter causes perltidy to ignore the length of side comments when
-setting line breaks.  The default, B<-niscl>, is to include the length of 
+setting line breaks.  The default, B<-niscl>, is to include the length of
 side comments when breaking lines to stay within the length prescribed
 by the B<-l=n> maximum line length parameter.  For example, the following
 long single line would remain intact with -l=80 and -iscl:
@@ -1600,7 +1730,7 @@ whereas without the -iscl flag the line will be broken:
      perltidy -l=80
         $vmsfile =~ s/;[\d\-]*$//
           ;    # Clip off version number; we can use a newer version as well
-   
+
 
 =item B<-hsc>, B<--hanging-side-comments>
 
@@ -1614,7 +1744,7 @@ comments", which are something like this:
 A comment is considered to be a hanging side comment if (1) it immediately
 follows a line with a side comment, or another hanging side comment, and
 (2) there is some leading whitespace on the line.
-To deactivate this feature, use B<-nhsc> or B<--nohanging-side-comments>.  
+To deactivate this feature, use B<-nhsc> or B<--nohanging-side-comments>.
 If block comments are preceded by a blank line, or have no leading
 whitespace, they will not be mistaken as hanging side comments.
 
@@ -1651,7 +1781,7 @@ for the C<if> and C<else> blocks, because they were below the 6 line
 cutoff limit for adding closing side comments.  This limit may be
 changed with the B<-csci> command, described below.
 
-The command B<-dcsc> (or B<--delete-closing-side-comments>) reverses this 
+The command B<-dcsc> (or B<--delete-closing-side-comments>) reverses this
 process and removes these comments.
 
 Several commands are available to modify the behavior of these two basic
@@ -1659,7 +1789,7 @@ commands, B<-csc> and B<-dcsc>:
 
 =over 4
 
-=item B<-csci=n>, or B<--closing-side-comment-interval=n> 
+=item B<-csci=n>, or B<--closing-side-comment-interval=n>
 
 where C<n> is the minimum number of lines that a block must have in
 order for a closing side comment to be added.  The default value is
@@ -1678,7 +1808,7 @@ C<n=6>.  To illustrate:
 Now the C<if> and C<else> blocks are commented.  However, now this has
 become very cluttered.
 
-=item B<-cscp=string>, or B<--closing-side-comment-prefix=string> 
+=item B<-cscp=string>, or B<--closing-side-comment-prefix=string>
 
 where string is the prefix used before the name of the block type.  The
 default prefix, shown above, is C<## end>.  This string will be added to
@@ -1687,7 +1817,7 @@ order to update, delete, and format them.  Any comment identified as a
 closing side comment will be placed just a single space to the right of
 its closing brace.
 
-=item B<-cscl=string>, or B<--closing-side-comment-list> 
+=item B<-cscl=string>, or B<--closing-side-comment-list>
 
 where C<string> is a list of block types to be tagged with closing side
 comments.  By default, all code block types preceded by a keyword or
@@ -1700,7 +1830,7 @@ affected by any B<-csc> or B<-dcsc> operation:
 
    -cscl="sub : BEGIN END"
 
-=item B<-csct=n>, or B<--closing-side-comment-maximum-text=n> 
+=item B<-csct=n>, or B<--closing-side-comment-maximum-text=n>
 
 The text appended to certain block types, such as an C<if> block, is
 whatever lies between the keyword introducing the block, such as C<if>,
@@ -1715,7 +1845,7 @@ first block is C< ( !defined( $_[0] )...>.  The existing limit of
 C<n=20> caused this text to be truncated, as indicated by the C<...>.  See
 the next flag for additional control of the abbreviated text.
 
-=item B<-cscb>, or B<--closing-side-comments-balanced> 
+=item B<-cscb>, or B<--closing-side-comments-balanced>
 
 As discussed in the previous item, when the
 closing-side-comment-maximum-text limit is exceeded the comment text must
@@ -1735,7 +1865,7 @@ help them by appending appropriate balancing structure:
 
 The default is B<-cscb>.
 
-=item B<-csce=n>, or B<--closing-side-comment-else-flag=n> 
+=item B<-csce=n>, or B<--closing-side-comment-else-flag=n>
 
 The default, B<n=0>, places the text of the opening C<if> statement after any
 terminal C<else>.
@@ -1748,10 +1878,10 @@ side comments.
 If B<n=1> is used, the results will be the same as B<n=2> whenever the
 resulting line length is less than the maximum allowed.
 
-=item B<-cscb>, or B<--closing-side-comments-balanced> 
+=item B<-cscb>, or B<--closing-side-comments-balanced>
 
 When using closing-side-comments, and the closing-side-comment-maximum-text
-limit is exceeded, then the comment text must be abbreviated.  
+limit is exceeded, then the comment text must be abbreviated.
 It is terminated with three dots if the B<-cscb> flag is negated:
 
   perltidy -csc -ncscb
@@ -1764,18 +1894,18 @@ flag tries to help them by appending appropriate terminal balancing structures:
   perltidy -csc -cscb
   } ## end foreach my $foo (sort { $b cmp $a ... })
 
-The default is B<-cscb>.  
+The default is B<-cscb>.
 
 
-=item B<-cscw>, or B<--closing-side-comment-warnings> 
+=item B<-cscw>, or B<--closing-side-comment-warnings>
 
 This parameter is intended to help make the initial transition to the use of
-closing side comments.  
+closing side comments.
 It causes two
 things to happen if a closing side comment replaces an existing, different
 closing side comment:  first, an error message will be issued, and second, the
 original side comment will be placed alone on a new specially marked comment
-line for later attention. 
+line for later attention.
 
 The intent is to avoid clobbering existing hand-written side comments
 which happen to match the pattern of closing side comments. This flag
@@ -1783,7 +1913,7 @@ should only be needed on the first run with B<-csc>.
 
 =back
 
-B<Important Notes on Closing Side Comments:> 
+B<Important Notes on Closing Side Comments:>
 
 =over 4
 
@@ -1797,7 +1927,7 @@ brace.  Certain closing styles, such as the use of cuddled elses
 
 Please note that adding or deleting of closing side comments takes
 place only through the commands B<-csc> or B<-dcsc>.  The other commands,
-if used, merely modify the behavior of these two commands.  
+if used, merely modify the behavior of these two commands.
 
 =item *
 
@@ -1842,9 +1972,9 @@ particularly useful for controlling how commented code is displayed.
 =item B<-sbc>, B<--static-block-comments>
 
 When B<-sbc> is used, a block comment with a special leading pattern, C<##> by
-default, will be treated specially. 
+default, will be treated specially.
 
-Comments so identified  are treated as follows: 
+Comments so identified  are treated as follows:
 
 =over 4
 
@@ -1856,12 +1986,12 @@ be indented, and otherwise it may be,
 =item *
 
 no new blank line will be
-inserted before such a comment, and 
+inserted before such a comment, and
 
 =item *
 
 such a comment will never become
-a hanging side comment.  
+a hanging side comment.
 
 =back
 
@@ -1877,7 +2007,7 @@ Without this convention, the above code would become
 
     @month_of_year = (   # -nsbc
         'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
-  
+
         ##  'Dec', 'Nov'
         'Nov', 'Dec'
     );
@@ -1889,8 +2019,8 @@ The default is to use B<-sbc>.  This may be deactivated with B<-nsbc>.
 
 This parameter defines the prefix used to identify static block comments
 when the B<-sbc> parameter is set.  The default prefix is C<##>,
-corresponding to C<-sbcp=##>.  The prefix is actually part of a perl 
-pattern used to match lines and it must either begin with C<#> or C<^#>.  
+corresponding to C<-sbcp=##>.  The prefix is actually part of a perl
+pattern used to match lines and it must either begin with C<#> or C<^#>.
 In the first case a prefix ^\s* will be added to match any leading
 whitespace, while in the second case the pattern will match only
 comments with no leading whitespace.  For example, to
@@ -1905,7 +2035,7 @@ be formed.
 
 A pattern which can be useful is:
 
-    -sbcp=^#{2,}[^\s#] 
+    -sbcp=^#{2,}[^\s#]
 
 This pattern requires a static block comment to have at least one character
 which is neither a # nor a space.  It allows a line containing only '#'
@@ -1941,7 +2071,7 @@ The default is B<-nssc>.
 
 This parameter defines the prefix used to identify static side comments
 when the B<-ssc> parameter is set.  The default prefix is C<##>,
-corresponding to C<-sscp=##>.  
+corresponding to C<-sscp=##>.
 
 Please note that B<-sscp> merely defines the pattern used to identify
 static side comments; it will not be used unless the switch B<-ssc> is
@@ -1994,6 +2124,13 @@ this:
 Additional text may appear on the special comment lines provided that it
 is separated from the marker by at least one space, as in the above examples.
 
+Any number of code-skipping or format-skipping sections may appear in a file.
+If an opening code-skipping or format-skipping comment is not followed by a
+corresponding closing comment, then skipping continues to the end of the file.
+If a closing code-skipping or format-skipping comment appears in a file but
+does not follow a corresponding opening comment, then it is treated as an
+ordinary comment without any special meaning.
+
 It is recommended to use B<--code-skipping> only if you need to hide a block of
 an extended syntax which would produce errors if parsed by perltidy, and use
 B<--format-skipping> otherwise.  This is because the B<--format-skipping>
@@ -2046,19 +2183,19 @@ format skipping.  The default is equivalent to -fsb='#<<<'.  The string that
 you enter must begin with a # and should be in quotes as necessary to get past
 the command shell of your system.  It is actually the leading text of a pattern
 that is constructed by appending a '\s', so you must also include backslashes
-for characters to be taken literally rather than as patterns.  
+for characters to be taken literally rather than as patterns.
 
 Some examples show how example strings become patterns:
 
  -fsb='#\{\{\{' becomes /^#\{\{\{\s/  which matches  #{{{ but not #{{{{
  -fsb='#\*\*'   becomes /^#\*\*\s/    which matches  #** but not #***
- -fsb='#\*{2,}' becomes /^#\*{2,}\s/  which matches  #** and #***** 
+ -fsb='#\*{2,}' becomes /^#\*{2,}\s/  which matches  #** and #*****
 
 =item B<-fse=string>,  B<--format-skipping-end=string>
 
 The B<-fse=string> is the corresponding parameter used to change the
 ending marker for format skipping.  The default is equivalent to
--fse='#<<<'.  
+-fse='#<<<'.
 
 The beginning and ending strings may be the same, but it is preferable
 to make them different for clarity.
@@ -2100,9 +2237,9 @@ Control>.
 If you do not want any changes to the line breaks within
 lines of code in your script, set
 B<-fnl>, and they will remain fixed, and the rest of the commands in
-this section and sections 
+this section and sections
 L<Controlling List Formatting>,
-L<Retaining or Ignoring Existing Line Breaks>. 
+L<Retaining or Ignoring Existing Line Breaks>.
 You may want to use B<-noll> with this.
 
 Note: If you also want to keep your blank lines exactly
@@ -2120,7 +2257,7 @@ alternatives:
   # -ce
   if ($task) {
       yyy();
-  } else {    
+  } else {
       zzz();
   }
 
@@ -2128,7 +2265,7 @@ alternatives:
   if ($task) {
        yyy();
   }
-  else {    
+  else {
        zzz();
   }
 
@@ -2137,8 +2274,8 @@ the preceding closing block brace and is followed by its own opening block brace
 on the same line.  Other keywords and function names which are formatted with
 this "cuddled" style are B<elsif>, B<continue>, B<catch>, B<finally>.
 
-Other block types can be formatted by specifying their names on a 
-separate parameter B<-cbl>, described in a later section.  
+Other block types can be formatted by specifying their names on a
+separate parameter B<-cbl>, described in a later section.
 
 Cuddling between a pair of code blocks requires that the closing brace of the
 first block start a new line.  If this block is entirely on one line in the
@@ -2162,14 +2299,14 @@ snippet.
 If there are blank lines between cuddled blocks they will be eliminated.  If
 there are comments after the closing brace where cuddling would occur then
 cuddling will be prevented.  If this occurs, cuddling will restart later in the
-chain if possible.  
+chain if possible.
 
 =item B<-cb>,   B<--cuddled-blocks>
 
-This flag is equivalent to B<-ce>. 
+This flag is equivalent to B<-ce>.
 
 
-=item B<-cbl>,    B<--cuddled-block-list>     
+=item B<-cbl>,    B<--cuddled-block-list>
 
 The built-in default cuddled block types are B<else, elsif, continue, catch, finally>.
 
@@ -2183,7 +2320,7 @@ be set to
 
 or equivalently
 
-  -cbl=sort,map,grep 
+  -cbl=sort,map,grep
 
 Note however that these particular block types are typically short so there might not be much
 opportunity for the cuddled format style.
@@ -2191,13 +2328,13 @@ opportunity for the cuddled format style.
 Using commas avoids the need to protect spaces with quotes.
 
 As a diagnostic check, the flag B<--dump-cuddled-block-list> or B<-dcbl> can be
-used to view the hash of values that are generated by this flag. 
+used to view the hash of values that are generated by this flag.
 
 Finally, note that the B<-cbl> flag by itself merely specifies which blocks are formatted
 with the cuddled format. It has no effect unless this formatting style is activated with
 B<-ce>.
 
-=item B<-cblx>,    B<--cuddled-block-list-exclusive>     
+=item B<-cblx>,    B<--cuddled-block-list-exclusive>
 
 When cuddled else formatting is selected with B<-ce>, setting this flag causes
 perltidy to ignore its built-in defaults and rely exclusively on the block types
@@ -2218,7 +2355,7 @@ to cause it to span multiple lines.  This parameter controls that decision. The
 options are:
 
    cbo=0  Never force a short block to break.
-   cbo=1  If the first of a pair of blocks is broken in the input file, 
+   cbo=1  If the first of a pair of blocks is broken in the input file,
           then break the second [DEFAULT].
    cbo=2  Break open all blocks for maximal cuddled formatting.
 
@@ -2232,27 +2369,53 @@ blocks.
 The option B<cbo=2> produces maximal cuddling but will not allow any short blocks.
 
 
-=item B<-bl>,    B<--opening-brace-on-new-line>     
+=item B<-bl>, B<--opening-brace-on-new-line>, or B<--brace-left>
 
-Use the flag B<-bl> to place the opening brace on a new line:
+Use the flag B<-bl> to place an opening block brace on a new line:
 
-  if ( $input_file eq '-' )    # -bl 
-  {                          
-      important_function();
+  if ( $input_file eq '-' )
+  {
+      ...
   }
 
-This flag applies to all structural blocks, including named sub's (unless
-the B<-sbl> flag is set -- see next item).
+By default it applies to all structural blocks except B<sort map grep eval> and
+anonymous subs.
 
-The default style, B<-nbl>, places an opening brace on the same line as
-the keyword introducing it.  For example,
+The default is B<-nbl> which places an opening brace on the same line as
+the keyword introducing it if possible.  For example,
 
-  if ( $input_file eq '-' ) {   # -nbl (default)
+  # default
+  if ( $input_file eq '-' ) {
+     ...
+  }
 
-=item B<-sbl>,    B<--opening-sub-brace-on-new-line>     
+When B<-bl> is set, the blocks to which this applies can be controlled with the
+parameters B<--brace-left-list> and B<-brace-left-exclusion-list> described in the next sections.
 
-The flag B<-sbl> can be used to override the value of B<-bl> for
-the opening braces of named sub's.  For example, 
+=item B<-bll=s>, B<--brace-left-list=s>
+
+Use this parameter to change the types of block braces for which the
+B<-bl> flag applies; see L<Specifying Block Types>.  For example,
+B<-bll='if elsif else sub'> would apply it to only C<if/elsif/else>
+and named sub blocks.  The default is all blocks, B<-bll='*'>.
+
+=item B<-blxl=s>, B<--brace-left-exclusion-list=s>
+
+Use this parameter to exclude types of block braces for which the
+B<-bl> flag applies; see L<Specifying Block Types>.  For example,
+the default settings B<-bll='*'> and B<-blxl='sort map grep eval asub'>
+mean all blocks except B<sort map grep eval> and anonymous sub blocks.
+
+Note that the lists B<-bll=s> and B<-blxl=s> control the behavior of the
+B<-bl> flag but have no effect unless the B<-bl> flag is set.
+
+=item B<-sbl>,    B<--opening-sub-brace-on-new-line>
+
+The flag B<-sbl> provides a shortcut way to turn on B<-bl> just for named
+subs.  The same effect can be achieved by turning on B<-bl>
+with the block list set as B<-bll='sub'>.
+
+For example,
 
  perltidy -sbl
 
@@ -2268,10 +2431,9 @@ produces this result:
     }
  }
 
-This flag is negated with B<-nsbl>.  If B<-sbl> is not specified,
-the value of B<-bl> is used.
+This flag is negated with B<-nsbl>, which is the default.
 
-=item B<-asbl>,    B<--opening-anonymous-sub-brace-on-new-line>     
+=item B<-asbl>,    B<--opening-anonymous-sub-brace-on-new-line>
 
 The flag B<-asbl> is like the B<-sbl> flag except that it applies
 to anonymous sub's instead of named subs. For example
@@ -2292,31 +2454,50 @@ produces this result:
 
 This flag is negated with B<-nasbl>, and the default is B<-nasbl>.
 
-=item B<-bli>,    B<--brace-left-and-indent>     
+=item B<-bli>,    B<--brace-left-and-indent>
 
-The flag B<-bli> is the same as B<-bl> but in addition it causes one 
-unit of continuation indentation ( see B<-ci> ) to be placed before 
+The flag B<-bli> is similar to the B<-bl> flag but in addition it causes one
+unit of continuation indentation ( see B<-ci> ) to be placed before
 an opening and closing block braces.
 
-For example,
+For example, perltidy -bli gives
 
-        if ( $input_file eq '-' )    # -bli
+        if ( $input_file eq '-' )
           {
             important_function();
           }
 
-By default, this extra indentation occurs for blocks of type:
-B<if>, B<elsif>, B<else>, B<unless>, B<for>, B<foreach>, B<sub>, 
-B<while>, B<until>, and also with a preceding label.  The next item
-shows how to change this.
+By default, this extra indentation occurs for block types:
+B<if>, B<elsif>, B<else>, B<unless>, B<while>, B<for>, B<foreach>, B<do>, and
+also B<named subs> and blocks preceded by a B<label>.  The next item shows how to
+change this.
+
+B<Note>: The B<-bli> flag is similar to the B<-bl> flag, with the difference being
+that braces get indented.  But these two flags are implemented independently,
+and have different default settings for historical reasons.  If desired, a
+mixture of effects can be achieved if desired by turning them both on with
+different B<-list> settings.  In the event that both settings are selected for
+a certain block type, the B<-bli> style has priority.
 
-=item B<-blil=s>,    B<--brace-left-and-indent-list=s>     
+=item B<-blil=s>,    B<--brace-left-and-indent-list=s>
 
 Use this parameter to change the types of block braces for which the
-B<-bli> flag applies; see L<Specifying Block Types>.  For example,
-B<-blil='if elsif else'> would apply it to only C<if/elsif/else> blocks.
+B<-bli> flag applies; see L<Specifying Block Types>.
+
+The default is B<-blil='if else elsif unless while for foreach do : sub'>.
+
+=item B<-blixl=s>, B<--brace-left-and-indent-exclusion-list=s>
+
+Use this parameter to exclude types of block braces for which the B<-bli> flag
+applies; see L<Specifying Block Types>.
 
-=item B<-bar>,    B<--opening-brace-always-on-right>     
+This might be useful in conjunction with selecting all blocks B<-blil='*'>.
+The default setting is B<-blixl=' '>, which does not exclude any blocks.
+
+Note that the two parameters B<-blil> and B<-blixl> control the behavior of
+the B<-bli> flag but have no effect unless the B<-bli> flag is set.
+
+=item B<-bar>,    B<--opening-brace-always-on-right>
 
 The default style, B<-nbl> places the opening code block brace on a new
 line if it does not fit on the same line as the opening keyword, like
@@ -2385,7 +2566,7 @@ before the opening brace according to the value given to the integer B<n>:
   -bbhb=2 break if list is 'complex' (see note below)
   -bbhb=3 always break
 
-For example, 
+For example,
 
     # perltidy -bbhb=3
     $romanNumerals =
@@ -2431,7 +2612,7 @@ B<-bbhbi=n> in the next section.
 
 Similar flags for controlling parens and square brackets are given in the subsequent section.
 
-=back   
+=back
 
 =item B<-bbhbi=n>,  B<--break-before-hash-brace-and-indent=n>
 
@@ -2500,7 +2681,7 @@ which is placed on a new line by that parameter.  The indentation is as follows:
   -bbpi=1 outdent by one continuation level
   -bbpi=2 indent one full indentation level
 
-=item B<-wn>,  B<--weld-nested-containers> 
+=item B<-wn>,  B<--weld-nested-containers>
 
 The B<-wn> flag causes closely nested pairs of opening and closing container
 symbols (curly braces, brackets, or parens) to be "welded" together, meaning
@@ -2513,7 +2694,7 @@ For example:
        # default formatting
         do {
             {
-                next if $x == $y;    
+                next if $x == $y;
             }
         } until $x++ > $z;
 
@@ -2529,7 +2710,7 @@ must either (1) be adjacent as in the above example, or (2) have an anonymous
 sub declaration following an outer opening container symbol which is not a
 code block brace, or (3) have an outer opening paren separated from the inner
 opening symbol by any single non-container symbol or something that looks like
-a function evaluation, as illustrated in the next examples.  
+a function evaluation, as illustrated in the next examples.
 
 Any container symbol may serve as both the inner container of one pair and as
 the outer container of an adjacent pair. Consequently, any number of adjacent
@@ -2596,25 +2777,25 @@ the same example adding B<-vtc=2> is
                 $sx * int( $xr->numify() ) & $sy * int( $yr->numify() ) ) ),
             $m ) );
 
-This format option is quite general but there are some limitations.  
+This format option is quite general but there are some limitations.
 
 One limitation is that any line length limit still applies and can cause long
-welded sections to be broken into multiple lines.  
+welded sections to be broken into multiple lines.
 
 Another limitation is that an opening symbol which delimits quoted text cannot
 be included in a welded pair.  This is because quote delimiters are treated
-specially in perltidy.  
+specially in perltidy.
 
 Finally, the stacking of containers defined by this flag have priority over
 any other container stacking flags.  This is because any welding is done first.
 
-=item B<-wnxl=s>,  B<--weld-nested-exclusion-list> 
+=item B<-wnxl=s>,  B<--weld-nested-exclusion-list>
 
 The B<-wnxl=s> flag provides some control over the types of containers which
 can be welded.  The B<-wn> flag by default is "greedy" in welding adjacent
 containers.  If it welds more types of containers than desired, this flag
 provides a capability to reduce the amount of welding by specifying a list
-of things which should B<not> be welded. 
+of things which should B<not> be welded.
 
 The logic in perltidy to apply this is straightforward.  As each container
 token is being considered for joining a weld, any exclusion rules are consulted
@@ -2631,7 +2812,7 @@ last represents a quoted list.  For example the string
   -wnxl='[ { q'
 
 means do B<NOT> include square-bracets, braces, or quotes in any welds.  The only unspecified
-container is '(', so this string means that only welds involving parens will be made. 
+container is '(', so this string means that only welds involving parens will be made.
 
 To illustrate, following welded snippet consists of a chain of three welded
 containers with types '(' '[' and 'q':
@@ -2676,7 +2857,7 @@ container symbol.  The possible letters are currently 'k', 'K', 'f', 'F',
 
 For example, compare
 
-        # perltidy -wn 
+        # perltidy -wn
         if ( defined( $_Cgi_Query{
             $Config{'methods'}{'authentication'}{'remote'}{'cgi'}{'username'}
         } ) )
@@ -2716,8 +2897,8 @@ main points:
 Opening tokens (except for block braces) are controlled by B<-vt=n>, or
 B<--vertical-tightness=n>, where
 
- -vt=0 always break a line after opening token (default). 
- -vt=1 do not break unless this would produce more than one 
+ -vt=0 always break a line after opening token (default).
+ -vt=1 do not break unless this would produce more than one
          step in indentation in a line.
  -vt=2 never break a line after opening token
 
@@ -2731,9 +2912,9 @@ reason is explained below.
 Closing tokens (except for block braces) are controlled by B<-vtc=n>, or
 B<--vertical-tightness-closing=n>, where
 
- -vtc=0 always break a line before a closing token (default), 
- -vtc=1 do not break before a closing token which is followed 
-        by a semicolon or another closing token, and is not in 
+ -vtc=0 always break a line before a closing token (default),
+ -vtc=1 do not break before a closing token which is followed
+        by a semicolon or another closing token, and is not in
         a list environment.
  -vtc=2 never break before a closing token.
  -vtc=3 Like -vtc=1 except always break before a closing token
@@ -2758,7 +2939,7 @@ length).
 
 =back
 
-Here are some examples: 
+Here are some examples:
 
     # perltidy -lp -vt=0 -vtc=0
     %romanNumerals = (
@@ -2801,14 +2982,14 @@ so the closing paren is placed on a new line.
 
 The difference between B<-vt=1> and B<-vt=2> is shown here:
 
-    # perltidy -lp -vt=1 
+    # perltidy -lp -vt=1
     $init->add(
                 mysprintf( "(void)find_threadsv(%s);",
                            cstring( $threadsv_names[ $op->targ ] )
                 )
     );
 
-    # perltidy -lp -vt=2 
+    # perltidy -lp -vt=2
     $init->add( mysprintf( "(void)find_threadsv(%s);",
                            cstring( $threadsv_names[ $op->targ ] )
                 )
@@ -2858,8 +3039,8 @@ for B<-pvtc=n -bvtc=n -sbvtc=n>.
 The B<-bbvt=n> flag is just like the B<-vt=n> flag but applies
 to opening code block braces.
 
- -bbvt=0 break after opening block brace (default). 
- -bbvt=1 do not break unless this would produce more than one 
+ -bbvt=0 break after opening block brace (default).
+ -bbvt=1 do not break unless this would produce more than one
          step in indentation in a line.
  -bbvt=2 do not break after opening block brace.
 
@@ -2897,7 +3078,7 @@ space-separated list of block types.  For more information on the
 possible values of this string, see L<Specifying Block Types>
 
 For example, if we want to just apply this style to C<if>,
-C<elsif>, and C<else> blocks, we could use 
+C<elsif>, and C<else> blocks, we could use
 C<perltidy -bli -bbvt=1 -bbvtl='if elsif else'>.
 
 There is no vertical tightness control for closing block braces; with
@@ -2941,7 +3122,7 @@ The flag B<-sot> is an abbreviation for B<-sop -sohb -sosb>.
 
 The flag B<-sobb> is an abbreviation for B<-bbvt=2 -bbvtl='*'>.  This
 will case a cascade of opening block braces to appear on a single line,
-although this an uncommon occurrence except in test scripts. 
+although this an uncommon occurrence except in test scripts.
 
 =item B<-sct>,  B<--stack-closing-tokens> and related flags
 
@@ -2990,7 +3171,7 @@ following controls can be used:
   -scbb or --stack-closing-block-brace
 
 The flag B<-sct> is an abbreviation for stacking the non-block closing
-tokens, B<-scp -schb -scsb>. 
+tokens, B<-scp -schb -scsb>.
 
 Stacking of closing block braces, B<-scbb>, causes a cascade of isolated
 closing block braces to be combined into a single line as in the following
@@ -3020,13 +3201,13 @@ B<-sct> flags do.
 By default, perltidy first deletes all old line break locations, and then it
 looks for good break points to match the desired line length.  Use B<-ndnl>
 or  B<--nodelete-old-newlines> to force perltidy to retain all old line break
-points.  
+points.
 
 =item B<-anl>,  B<--add-newlines>
 
 By default, perltidy will add line breaks when necessary to create
 continuations of long lines and to improve the script appearance.  Use
-B<-nanl> or B<--noadd-newlines> to prevent any new line breaks.  
+B<-nanl> or B<--noadd-newlines> to prevent any new line breaks.
 
 This flag does not prevent perltidy from eliminating existing line
 breaks; see B<--freeze-newlines> to completely prevent changes to line
@@ -3049,7 +3230,7 @@ command-line parameter always overwrites the previous one before
 perltidy ever sees it.
 
 By default, perltidy breaks B<after> these token types:
-  % + - * / x != == >= <= =~ !~ < >  | & 
+  % + - * / x != == >= <= =~ !~ < >  | &
   = **= += *= &= <<= &&= -= /= |= >>= ||= //= .= %= ^= x=
 
 And perltidy breaks B<before> these token types by default:
@@ -3060,7 +3241,7 @@ rather than before it, the command line would be
 
   -wba="."
 
-As another example, the following command would cause a break before 
+As another example, the following command would cause a break before
 math operators C<'+'>, C<'-'>, C<'/'>, and C<'*'>:
 
   -wbb="+ - / *"
@@ -3084,7 +3265,7 @@ B<-bbao> or B<--break-before-all-operators>.
 
 The -baao sets the default to be to break after all of the following operators:
 
-    % + - * / x != == >= <= =~ !~ < > | & 
+    % + - * / x != == >= <= =~ !~ < > | &
     = **= += *= &= <<= &&= -= /= |= >>= ||= //= .= %= ^= x=
     . : ? && || and or err xor
 
@@ -3094,6 +3275,24 @@ with the B<-wba> and B<-wbb> flags.  For example, to break before all operators
 except an B<=> one could use --bbao -wba='=' rather than listing every
 single perl operator except B<=> on a -wbb flag.
 
+=item B<bal=n, --break-after-labels=n>
+
+This flag controls whether or not a line break occurs after a label. There
+are three possible valuse for B<n>:
+
+  -bal=0  break if there is a break in the input [DEFAULt]
+  -bal=1  always break after a label
+  -bal=2  never break after a label
+
+For example,
+
+      # perltidy -bal=1
+      RETURN:
+        return;
+
+      # perltidy -bal=2
+      RETURN: return;
+
 =back
 
 =head2 Controlling List Formatting
@@ -3123,20 +3322,22 @@ will flatten this down to one line:
     # perltidy (default)
     my @list = ( 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, );
 
-This formatting loses important information.  If we place a side comment on one
-of the lines, for example, we get the following result with with default formatting 
-parameters:
+This formatting loses the nice structure.  If we place a side comment anywhere
+between the opening and closing parens, the original line break points are
+retained.  For example,
 
     my @list = (
-        1,    # a side comment, comment, or blank keeps list intact
+        1,    # a side comment forces the original line breakpoints to be kept
         1, 1,
         1, 2, 1,
         1, 3, 3, 1,
         1, 4, 6, 4, 1,
     );
 
+The side comment can be a single hash symbol without any text.
 We could achieve the same result with a blank line or full comment
-anywhere between the opening and closing parens.
+anywhere between the opening and closing parens.  Vertical alignment
+of the list items will still occur if possible.
 
 For another possibility see
 the -fs flag in L<Skipping Selected Sections of Code>.
@@ -3146,7 +3347,7 @@ the -fs flag in L<Skipping Selected Sections of Code>.
 =item B<-boc>,  B<--break-at-old-comma-breakpoints>
 
 The B<-boc> flag is another way to prevent comma-separated lists from being
-reformatted.  Using B<-boc> on the above example, plus additional flags to retain 
+reformatted.  Using B<-boc> on the above example, plus additional flags to retain
 the original style, yields
 
     # perltidy -boc -lp -pt=2 -vt=1 -vtc=1
@@ -3156,9 +3357,8 @@ the original style, yields
                 1, 3, 3, 1,
                 1, 4, 6, 4, 1,);
 
-A disadvantage of this flag is that all tables in the file
-must already be nicely formatted.  
-
+A disadvantage of this flag compared to the methods discussed above is that all
+tables in the file must already be nicely formatted.
 
 =item B<-mft=n>,  B<--maximum-fields-per-table=n>
 
@@ -3172,7 +3372,7 @@ be introduced somewhere to freeze the formatting in future applications
 of perltidy.
 
     # perltidy -mft=2
-    @month_of_year = (    
+    @month_of_year = (
         'Jan', 'Feb',
         'Mar', 'Apr',
         'May', 'Jun',
@@ -3189,12 +3389,12 @@ commas.  This parameter can be used to control how perltidy breaks at
 these commas.  (However, it will have no effect if old comma breaks are
 being forced because B<-boc> is used).  The possible values of B<n> are:
 
- n=0 break at all commas after =>  
+ n=0 break at all commas after =>
  n=1 stable: break at all commas after => if container is open,
      EXCEPT FOR one-line containers
  n=2 break at all commas after =>, BUT try to form the maximum
      one-line container lengths
- n=3 do not treat commas after => specially at all 
+ n=3 do not treat commas after => specially at all
  n=4 break everything: like n=0 but ALSO break a short container with
      a => not followed by a comma when -vt=0 is used
  n=5 stable: like n=1 but ALSO break at open one-line containers when
@@ -3265,7 +3465,7 @@ short expressions into a single line.
 
 For example, given this snippet:
 
-    return unless $cmd = $cmd || ($dot 
+    return unless $cmd = $cmd || ($dot
         && $Last_Shell) || &prompt('|');
 
     # perltidy -bol [default]
@@ -3298,8 +3498,8 @@ It will B<keep> these breaks, rather than become this:
       'track.id' => {-ident => 'none_search.id'},
     })->as_query;
 
-This flag will also look for and keep a 'cuddled' style of calls, 
-in which lines begin with a closing paren followed by a call arrow, 
+This flag will also look for and keep a 'cuddled' style of calls,
+in which lines begin with a closing paren followed by a call arrow,
 as in this example:
 
   # perltidy -bom -wn
@@ -3311,7 +3511,7 @@ as in this example:
       'track.id' => { -ident => 'none_search.id' },
   } )->as_query;
 
-You may want to include the B<-weld-nested-containers> flag in this case to keep 
+You may want to include the B<-weld-nested-containers> flag in this case to keep
 nested braces and parens together, as in the last line.
 
 =item B<-bos>,  B<--break-at-old-semicolon-breakpoints>
@@ -3331,7 +3531,7 @@ The result using B<perltidy -bos> keeps the isolated semicolon:
   $z = sqrt( $x**2 + $y**2 )
     ;
 
-The default is not to do this, B<-nbos>. 
+The default is not to do this, B<-nbos>.
 
 
 =item B<-bok>,  B<--break-at-old-keyword-breakpoints>
@@ -3361,13 +3561,13 @@ at the ':'s will be retained:
 If the attributes are on a single line in the source code then they will remain
 on a single line if possible.
 
-To prevent this, and thereby always form longer lines, use B<-nboa>.  
+To prevent this, and thereby always form longer lines, use B<-nboa>.
 
 =item B<Keeping old breakpoints at specific token types>
 
-Two command line parameters provide detailed control over whether
-perltidy should keep an old line break before or after a specific
-token type:
+It is possible to override the choice of line breaks made by perltidy, and
+force it to follow certain line breaks in the input stream, with these two
+parameters:
 
 B<-kbb=s> or B<--keep-old-breakpoints-before=s>, and
 
@@ -3405,14 +3605,70 @@ For example, given the script:
         ...;
       };
 
+For the container tokens '{', '[' and '(' and, their closing counterparts, use the token symbol. Thus,
+the command to keep a break after all opening parens is:
+
+   perltidy -kba='('
+
+It is possible to be more specific in matching parentheses by preceding them
+with a letter.  The possible letters are 'k', 'K', 'f', 'F', 'w', and 'W', with
+these meanings (these are the same as used in the
+B<--weld-nested-exclusion-list> and B<--line-up-parentheses-exclusion-list>
+parameters):
+
+ 'k' matches if the previous nonblank token is a perl builtin keyword (such as 'if', 'while'),
+ 'K' matches if 'k' does not, meaning that the previous token is not a keyword.
+ 'f' matches if the previous token is a function other than a keyword.
+ 'F' matches if 'f' does not.
+ 'w' matches if either 'k' or 'f' match.
+ 'W' matches if 'w' does not.
+
+So for example the the following parameter will keep breaks after opening function call
+parens:
+
+   perltidy -kba='f('
+
+B<NOTE>: To match all opening curly braces, and no other opening tokens, please
+prefix the brace it with an asterisk, like this: '*{'.  Otherwise a warning
+message will occur. This is necessary to avoid problems while the input scheme
+is being updated and generalized.  A single bare curly brace previously matched
+all container tokens, and tentatively still does. Likewise, to match all
+closing curly braces, and no other closing tokens, use '*}'.
+
 =item B<-iob>,  B<--ignore-old-breakpoints>
 
 Use this flag to tell perltidy to ignore existing line breaks to the
 maximum extent possible.  This will tend to produce the longest possible
 containers, regardless of type, which do not exceed the line length
-limit. But please note that this parameter has priority over all
+limit.  But please note that this parameter has priority over all
 other parameters requesting that certain old breakpoints be kept.
 
+To illustrate, consider the following input text:
+
+    has subcmds => (
+        is => 'ro',
+        default => sub { [] },
+    );
+
+The default formatting will keep the container broken, giving
+
+    # perltidy [default]
+    has subcmds => (
+        is      => 'ro',
+        default => sub { [] },
+    );
+
+If old breakpoints are ignored, the list will be flattened:
+
+    # perltidy -iob
+    has subcmds => ( is => 'ro', default => sub { [] }, );
+
+Besides flattening lists, this parameter also applies to lines broken
+at certain logical breakpoints such as 'if' and 'or'.
+
+Even if this is parameter is not used globally, it provides a convenient way to
+flatten selected lists from within an editor.
+
 =item B<-kis>,  B<--keep-interior-semicolons>
 
 Use the B<-kis> flag to prevent breaking at a semicolon if
@@ -3438,7 +3694,7 @@ With B<perltidy -kis> the multiple statements are retained:
     dbmclose(%expanded);   undef %expanded;
 
 The statements are still subject to the specified value
-of B<maximum-line-length> and will be broken if this 
+of B<maximum-line-length> and will be broken if this
 maximum is exceeded.
 
 =back
@@ -3447,7 +3703,7 @@ maximum is exceeded.
 
 Blank lines can improve the readability of a script if they are carefully
 placed.  Perltidy has several commands for controlling the insertion,
-retention, and removal of blank lines.  
+retention, and removal of blank lines.
 
 =over 4
 
@@ -3480,7 +3736,7 @@ This parameter interacts with the value B<k> of the parameter B<--maximum-consec
 
 2. If the number of old blank lines in the script is less than B<n> then
 additional blanks will be inserted to make the total B<n> regardless of the
-value of B<-mbl=k>.  
+value of B<-mbl=k>.
 
 3. If the number of old blank lines in the script equals or exceeds B<n> then
 this parameter has no effect, however the total will not exceed
@@ -3490,7 +3746,7 @@ value specified on the B<-mbl=k> flag.
 =item B<-blbp=n>,  B<--blank-lines-before-packages=n>
 
 The parameter B<-blbp=n> requests that least B<n> blank lines precede a package
-which does not follow a comment.  The default is B<-blbp=1>.  
+which does not follow a comment.  The default is B<-blbp=1>.
 
 This parameter interacts with the value B<k> of the parameter
 B<--maximum-consecutive-blank-lines=k> (B<-mbl=k>) in the same way as described
@@ -3500,10 +3756,10 @@ for the previous item B<-blbs=n>.
 =item B<-bbs>,  B<--blanks-before-subs>
 
 For compatibility with previous versions, B<-bbs> or B<--blanks-before-subs>
-is equivalent to F<-blbp=1> and F<-blbs=1>.  
+is equivalent to F<-blbp=1> and F<-blbs=1>.
 
-Likewise, B<-nbbs> or B<--noblanks-before-subs> 
-is equivalent to F<-blbp=0> and F<-blbs=0>.  
+Likewise, B<-nbbs> or B<--noblanks-before-subs>
+is equivalent to F<-blbp=0> and F<-blbs=0>.
 
 =item B<-bbb>,  B<--blanks-before-blocks>
 
@@ -3534,7 +3790,7 @@ This is negated with B<-nbbb> or  B<--noblanks-before-blocks>.
 
 =item B<-lbl=n> B<--long-block-line-count=n>
 
-This controls how often perltidy is allowed to add blank lines before 
+This controls how often perltidy is allowed to add blank lines before
 certain block types (see previous section).  The default is 8.  Entering
 a value of B<0> is equivalent to entering a very large number.
 
@@ -3584,7 +3840,7 @@ this using
 Now suppose the script continues to be developed, but at some later date we
 decide we don't want these spaces after all. We might expect that running with
 the flags B<-blao=0> and B<-blbc=0> will undo them.  However, by default
-perltidy retains single blank lines, so the blank lines remain.  
+perltidy retains single blank lines, so the blank lines remain.
 
 We can easily fix this by telling perltidy to ignore old blank lines by
 including the added parameter B<-kbl=0> and rerunning. Then the unwanted blank
@@ -3592,7 +3848,7 @@ lines will be gone.  However, this will cause all old blank lines to be
 ignored, perhaps even some that were added by hand to improve formatting. So
 please be cautious when using these parameters.
 
-=item B<-mbl=n> B<--maximum-consecutive-blank-lines=n>   
+=item B<-mbl=n> B<--maximum-consecutive-blank-lines=n>
 
 This parameter specifies the maximum number of consecutive blank lines which
 will be output within code sections of a script.  The default is n=1.  If the
@@ -3602,12 +3858,12 @@ then no blank lines will be output (unless all old blank lines are retained
 with the B<-kbl=2> flag of the next section).
 
 This flag obviously does not apply to pod sections,
-here-documents, and quotes.  
+here-documents, and quotes.
 
 =item B<-kbl=n>,  B<--keep-old-blank-lines=n>
 
 The B<-kbl=n> flag gives you control over how your existing blank lines are
-treated.  
+treated.
 
 The possible values of B<n> are:
 
@@ -3615,7 +3871,7 @@ The possible values of B<n> are:
  n=1 stable: keep old blanks, but limited by the value of the B<-mbl=n> flag
  n=2 keep all old blank lines, regardless of the value of the B<-mbl=n> flag
 
-The default is B<n=1>.  
+The default is B<n=1>.
 
 =item B<-sob>,  B<--swallow-optional-blank-lines>
 
@@ -3640,7 +3896,7 @@ following parameters:
 
 B<-kgbl=s> or B<--keyword-group-blanks-list=s>; B<s> is a quoted string of keywords
 
-B<-kgbs=s> or B<--keyword-group-blanks-size=s>; B<s> gives the number of keywords required to form a group.  
+B<-kgbs=s> or B<--keyword-group-blanks-size=s>; B<s> gives the number of keywords required to form a group.
 
 B<-kgbb=n> or B<--keyword-group-blanks-before=n>; B<n> = (0, 1, or 2) controls a leading blank
 
@@ -3711,20 +3967,25 @@ space separated list of keywords.  The default set is B<s="use require local
 our my">, but any list of keywords may be used. Comment lines may also be included in a keyword group, even though they are not keywords.  To include ordinary block comments, include the symbol B<BC>. To include static block comments (which normally begin with '##'), include the symbol B<SBC>.
 
 B<-kgbs=s> or B<--keyword-group-blanks-size=s>, where B<s> is a string
-describing the number of consecutive keyword statements forming a group.  If
-B<s> is an integer then it is the minimum number required for a group.  A
-maximum value may also be given with the format B<s=min.max>, where B<min> is
-the minimum number and B<max> is the maximum number, and the min and max values
-are separated by one or more dots.  No groups will be found if the maximum is
-less than the minimum.  The maximum is unlimited if not given.  The default is
-B<s=5>.  Some examples:
+describing the number of consecutive keyword statements forming a group (Note:
+statements separated by blank lines in the input file are considered
+consecutive for purposes of this count).  If B<s> is an integer then it is the
+minimum number required for a group.  A maximum value may also be given with
+the format B<s=min.max>, where B<min> is the minimum number and B<max> is the
+maximum number, and the min and max values are separated by one or more dots.
+No groups will be found if the maximum is less than the minimum.  The maximum
+is unlimited if not given.  The default is B<s=5>.  Some examples:
 
     s      min   max         number for group
     3      3     unlimited   3 or more
     1.1    1     1           1
     1..3   1     3           1 to 3
     1.0    1     0           (no match)
-    
+
+There is no really good default value for this parameter.  If it is set too
+small, then an excessive number of blank lines may be generated.  However, some
+users may prefer reducing the value somewhat below the default, perhaps to
+B<s=3>.
 
 B<-kgbb=n> or B<--keyword-group-blanks-before=n> specifies whether
 a blank should appear before the first line of the group, as follows:
@@ -3750,7 +4011,7 @@ blank lines that exist in the the group when it is first scanned.  When
 statements are initially scanned, any existing blank lines are included in the
 collection.  Any such orignial blank lines will be deleted before any other
 insertions are made when the parameter B<-kgbd> is set.  The default is not to
-do this, B<-nkgbd>.  
+do this, B<-nkgbd>.
 
 B<-kgbr=n> or B<--keyword-group-blanks-repeat-count=n> specifies B<n>, the
 maximum number of times this logic will be applied to any file.  The special
@@ -3761,13 +4022,13 @@ statements in the top part of a module for example.
 
 B<-kgb> or B<--keyword-group-blanks> is an abbreviation equivalent to setting
 B<-kgbb=1 -kgba=1 -kgbi>.  This turns on keyword group formatting with a set of
-default values.  
+default values.
 
 B<-nkgb> or B<--nokeyword-group-blanks> is equivalent to B<-kgbb=0 -kgba
 nkgbi>.  This flag turns off keyword group blank lines and is the default
-setting. 
+setting.
 
-Here are a few notes about the functioning of this technique.  
+Here are a few notes about the functioning of this technique.
 
 =over 4
 
@@ -3799,7 +4060,7 @@ The introduction of blank lines does not occur if it would conflict with other
 input controls or code validity. For example, a blank line will not be placed
 within a here-doc or within a section of code marked with format skipping
 comments.  And in general, a blank line will only be introduced at the end of a
-group if the next statement is a line of code. 
+group if the next statement is a line of code.
 
 =item *
 
@@ -3831,18 +4092,20 @@ style overrides the default style with the following parameters:
 
     -lp -bl -noll -pt=2 -bt=2 -sbt=2 -icp
 
+To use this style with B<-xlp> instead of B<-lp> use B<-gnu -xlp>.
+
 =item B<-pbp>, B<--perl-best-practices>
 
 B<-pbp> is an abbreviation for the parameters in the book B<Perl Best Practices>
 by Damian Conway:
 
     -l=78 -i=4 -ci=4 -st -se -vt=2 -cti=0 -pt=1 -bt=1 -sbt=1 -bbt=1 -nsfs -nolq
-    -wbb="% + - * / x != == >= <= =~ !~ < > | & = 
+    -wbb="% + - * / x != == >= <= =~ !~ < > | & =
           **= += *= &= <<= &&= -= /= |= >>= ||= //= .= %= ^= x="
 
 Please note that this parameter set includes -st and -se flags, which make
 perltidy act as a filter on one file only.  These can be overridden by placing
-B<-nst> and/or B<-nse> after the -pbp parameter. 
+B<-nst> and/or B<-nse> after the -pbp parameter.
 
 Also note that the value of continuation indentation, -ci=4, is equal to the
 value of the full indentation, -i=4.  It is recommended that the either (1) the
@@ -3893,6 +4156,7 @@ to include it separately.
 There are a few points to note regarding one-line blocks.  A one-line
 block is something like this,
 
+    if ( -e $file ) { print "'$file' exists\n" }
 
 where the contents within the curly braces is short enough to fit
 on a single line.
@@ -3905,7 +4169,7 @@ one-line block style of the input file.
 If an existing one-line block is longer than the maximum line length,
 however, it will be broken into multiple lines.  When this happens, perltidy
 checks for and adds any optional terminating semicolon (unless the B<-nasc>
-option is used) if the block is a code block.  
+option is used) if the block is a code block.
 
 The main exception is that perltidy will attempt to form new one-line
 blocks following the keywords C<map>, C<eval>, and C<sort>, because
@@ -3924,7 +4188,7 @@ the problem.
 Sometimes it might be desirable to convert a script to have one-line blocks
 whenever possible.  Although there is currently no flag for this, a simple
 workaround is to execute perltidy twice, once with the flag B<-noadd-newlines>
-and then once again with normal parameters, like this:  
+and then once again with normal parameters, like this:
 
      cat infile | perltidy -nanl | perltidy >outfile
 
@@ -3953,7 +4217,7 @@ This shows that blocks with a single statement become one-line blocks.
 This flag controls the placement of semicolons at the end of one-line blocks.
 Semicolons are optional before a closing block brace, and frequently they are
 omitted at the end of a one-line block containing just a single statement.
-By default, perltidy follows the input file regarding these semicolons, 
+By default, perltidy follows the input file regarding these semicolons,
 but this behavior can be controlled by this flag.  The values of n are:
 
   n=0 remove terminal semicolons in one-line blocks having a single statement
@@ -3987,7 +4251,7 @@ For the above example, the default formatting (B<-olbn=0>) is
     }
 
 If the parameter B<-olbn=1> is given, then the line will be left intact if it
-is a single line in the source, or it will be broken into multiple lines if it 
+is a single line in the source, or it will be broken into multiple lines if it
 is broken in multiple lines in the source.
 
 
@@ -4001,13 +4265,13 @@ similar lines to improve readability.  For example, the "fat commas" are
 aligned in the following statement:
 
         $data = $pkg->new(
-            PeerAddr => join( ".", @port[ 0 .. 3 ] ),   
+            PeerAddr => join( ".", @port[ 0 .. 3 ] ),
             PeerPort => $port[4] * 256 + $port[5],
             Proto    => 'tcp'
         );
 
-Vertical alignment can be completely turned off using B<-novalign>, a flag
-mainly intended for debugging.  However, vertical alignment can be forced to
+Vertical alignment can be completely turned off using the B<-novalign> flag
+mentioned below.  However, vertical alignment can be forced to
 stop and restart by selectively introducing blank lines.  For example, a blank
 has been inserted in the following code to keep somewhat similar things
 aligned.
@@ -4029,7 +4293,7 @@ blank space to increase.  So if a particular space is removed by one of the
 existing controls then vertical alignment cannot occur. Likewise, if a space is
 added with one of the controls, then vertical alignment might occur.
 
-For example, 
+For example,
 
         # perltidy -nwls='=>'
         $data = $pkg->new(
@@ -4038,6 +4302,105 @@ For example,
             Proto=> 'tcp'
         );
 
+=over 4
+
+=item B<Completely turning off vertical alignment with -novalign>
+
+The default is to use vertical alignment, but bertical alignment can be
+completely turned of with the B<-novalign> flag.
+
+A lower level of control of vertical alignment is possible with three parameters
+B<-vc>, B<-vsc>, and B<-vbc>. These independently control alignment
+of code, side comments and block comments.  They are described in the
+next section.
+
+The parameter B<-valign> is in fact an alias for B<-vc -vsc -vbc>, and its
+negative B<-novalign> is an alias for B<-nvc -nvsc -nvbc>.
+
+=item B<Controlling code alignment with --valign-code or -vc>
+
+The B<-vc> flag enables alignment of code symbols such as B<=>.  The default is B<-vc>.
+For detailed control of which symbols to align, see the B<-valign-exclude-list> parameter
+below.
+
+=item B<Controlling side comment alignment with --valign-side-comments or -vsc>
+
+The B<-vsc> flag enables alignment of side comments and is enabled by default.  If side
+comment aligment is disabled with B<-nvsc> they will appear at a fixed space from the
+preceding code token.  The default is B<-vsc>
+
+=item B<Controlling block comment alignment with --valign-block-comments or -vbc>
+
+When B<-vbc> is enabled, block comments can become aligned for example if one
+comment of a consecutive sequence of comments becomes outdented due a length in
+excess of the maximum line length.  If this occurs, the entire group of
+comments will remain aligned and be outdented by the same amount.  This coordinated
+alignment will not occur if B<-nvbc> is set.  The default is B<-vbc>.
+
+=item B<Finer alignment control with --valign-exclusion-list=s or -vxl=s and --valign-inclusion-list=s or -vil=s>
+
+More detailed control of alignment types is available with these two
+parameters.  Most of the vertical alignments in typical programs occur at one
+of the tokens ',', '=', and '=>', but many other alignments are possible and are given in the following list:
+
+  = **= += *= &= <<= &&= -= /= |= >>= ||= //= .= %= ^= x=
+  { ( ? : , ; => && || ~~ !~~ =~ !~ // <=> ->
+  if unless and or err for foreach while until
+
+These alignments are all enabled by default, but they can be selectively disabled by including one or more of these tokens in the space-separated list B<valign-exclusion-list=s>.
+For example, the following would prevent alignment at B<=> and B<if>:
+
+  --valign-exclusion-list='= if'
+
+If it is simpler to specify only the token types which are to be aligned, then
+include the types which are to be aligned in the list of B<--valign-inclusion-list>.
+You may leave the B<valign-exclusion-list> undefined, or use the special symbol B<*> for the exclusion list.
+For example, the following parameters enable alignment only at commas and 'fat commas':
+
+  --valign-inclusion-list=', =>'
+  --valign-exclusion-list='*'     ( this is optional and may be omitted )
+
+These parameter lists should consist of space-separated tokens from the above
+list of possible alignment tokens, or a '*'.  If an unrecognized token
+appears, it is simply ignored. And if a specific token is entered in both lists by
+mistake then the exclusion list has priority.
+
+The default values of these parameters enable all alignments and are equivalent to
+
+  --valign-exclusion-list=' '
+  --valign-inclusion-list='*'
+
+To illustrate, consider the following snippet with default formatting
+
+    # perltidy
+    $co_description = ($color) ? 'bold cyan'  : '';           # description
+    $co_prompt      = ($color) ? 'bold green' : '';           # prompt
+    $co_unused      = ($color) ? 'on_green'   : 'reverse';    # unused
+
+To exclude all alignments except the equals (i.e., include only equals) we could use:
+
+    # perltidy -vil='='
+    $co_description = ($color) ? 'bold cyan' : '';          # description
+    $co_prompt      = ($color) ? 'bold green' : '';         # prompt
+    $co_unused      = ($color) ? 'on_green' : 'reverse';    # unused
+
+To exclude only the equals we could use:
+
+    # perltidy -vxl='='
+    $co_description = ($color) ? 'bold cyan' : '';     # description
+    $co_prompt = ($color) ? 'bold green' : '';         # prompt
+    $co_unused = ($color) ? 'on_green' : 'reverse';    # unused
+
+Notice in this last example that although only the equals alignment was
+excluded, the ternary alignments were also lost.  This happens because the
+vertical aligner sweeps from left-to-right and usually stops if an important
+alignment cannot be made for some reason.
+
+But also notice that side comments remain aligned because their alignment is
+controlled separately with the parameter B<--valign-side_comments> described above.
+
+=back
+
 =head2 Other Controls
 
 =over 4
@@ -4065,7 +4428,7 @@ hash-bang will be retained (even if they are in the form of comments).
 
 When perltidy writes a formatted text file, it has the ability to also
 send selected text to a file with a F<.TEE> extension.  This text can
-include comments and pod documentation.  
+include comments and pod documentation.
 
 The command B<-tac> or  B<--tee-all-comments> will write all comments
 B<and> all pod documentation.
@@ -4077,7 +4440,7 @@ The commands which write comments (but not pod) are: B<-tbc> or
 B<--tee-block-comments> and B<-tsc> or  B<--tee-side-comments>.
 (Hanging side comments will be written with side comments here.)
 
-The negatives of these commands also work, and are the defaults.  
+The negatives of these commands also work, and are the defaults.
 
 =item B<Using a F<.perltidyrc> command file>
 
@@ -4085,7 +4448,7 @@ If you use perltidy frequently, you probably won't be happy until you
 create a F<.perltidyrc> file to avoid typing commonly-used parameters.
 Perltidy will first look in your current directory for a command file
 named F<.perltidyrc>.  If it does not find one, it will continue looking
-for one in other standard locations.  
+for one in other standard locations.
 
 These other locations are system-dependent, and may be displayed with
 the command C<perltidy -dpro>.  Under Unix systems, it will first look
@@ -4132,7 +4495,7 @@ Here is an example of a F<.perltidyrc> file:
   -sbt=0 # square brackets not tight
 
 The parameters in the F<.perltidyrc> file are installed first, so any
-parameters given on the command line will have priority over them.  
+parameters given on the command line will have priority over them.
 
 To avoid confusion, perltidy ignores any command in the .perltidyrc
 file which would cause some kind of dump and an exit.  These are:
@@ -4140,17 +4503,17 @@ file which would cause some kind of dump and an exit.  These are:
  -h -v -ddf -dln -dop -dsn -dtt -dwls -dwrs -ss
 
 There are several options may be helpful in debugging a F<.perltidyrc>
-file:  
+file:
 
 =over 4
 
 =item *
 
 A very helpful command is B<--dump-profile> or B<-dpro>.  It writes a
-list of all configuration filenames tested to standard output, and 
+list of all configuration filenames tested to standard output, and
 if a file is found, it dumps the content to standard output before
 exiting.  So, to find out where perltidy looks for its configuration
-files, and which one if any it selects, just enter 
+files, and which one if any it selects, just enter
 
   perltidy -dpro
 
@@ -4162,7 +4525,7 @@ line.  Then rename the desired file to F<.perltidyrc> when finished.
 
 =item *
 
-The parameters in the F<.perltidyrc> file can be switched off with 
+The parameters in the F<.perltidyrc> file can be switched off with
 the B<-npro> option.
 
 =item *
@@ -4207,7 +4570,7 @@ provides a way to format a long 'one liner' when perltidy is invoked with
 
        perltidy --oneliner ...
 
-(Either C<-oneliner> or C<--oneliner> may be used).   
+(Either C<-oneliner> or C<--oneliner> may be used).
 
 =item Skipping leading non-perl commands with B<-x> or B<--look-for-hash-bang>
 
@@ -4262,19 +4625,19 @@ string.
 
 B<--dump-defaults> or B<-ddf> will write the default option set to standard output and quit
 
-B<--dump-profile> or B<-dpro>  will write the name of the current 
+B<--dump-profile> or B<-dpro>  will write the name of the current
 configuration file and its contents to standard output and quit.
 
 B<--dump-options> or B<-dop>  will write current option set to standard
-output and quit.  
+output and quit.
 
-B<--dump-long-names> or B<-dln>  will write all command line long names (passed 
+B<--dump-long-names> or B<-dln>  will write all command line long names (passed
 to Get_options) to standard output and quit.
 
-B<--dump-short-names>  or B<-dsn> will write all command line short names 
+B<--dump-short-names>  or B<-dsn> will write all command line short names
 to standard output and quit.
 
-B<--dump-token-types> or B<-dtt>  will write a list of all token types 
+B<--dump-token-types> or B<-dtt>  will write a list of all token types
 to standard output and quit.
 
 B<--dump-want-left-space> or B<-dwls>  will write the hash %want_left_space
@@ -4286,7 +4649,7 @@ to standard output and quit.  See the section on controlling whitespace
 around tokens.
 
 B<--no-memoize> or B<-nmem>  will turn of memoizing.
-Memoization can reduce run time when running perltidy repeatedly in a 
+Memoization can reduce run time when running perltidy repeatedly in a
 single process.  It is on by default but can be deactivated for
 testing with B<-nmem>.
 
@@ -4299,7 +4662,7 @@ to allow timestamps (B<--timestamp> or B<-ts>).
 B<--file-size-order> or B<-fso> will cause files to be processed in order of
 increasing size, when multiple files are being processed.  This is useful
 during program development, when large numbers of files with varying sizes are
-processed, because it can reduce virtual memory usage. 
+processed, because it can reduce virtual memory usage.
 
 B<--maximum-file-size-mb=n> or B<-maxfs=n> specifies the maximum file size in
 megabytes that perltidy will attempt to format. This parameter is provided to
@@ -4311,7 +4674,7 @@ MB for example would be
 
   perltidy -maxfs=20
 
-This only applies to files specified by filename on the command line. 
+This only applies to files specified by filename on the command line.
 
 B<--maximum-level-errors=n> or B<-maxle=n> specifies the maximum number of
 indentation level errors are allowed before perltidy skips formatting and just
@@ -4334,20 +4697,20 @@ For example, the following script has level error of 3 and will be output verbat
 
 B<--maximum-unexpected-errors=n> or B<-maxue=n> specifies the maximum number of
 unexpected tokenization errors are allowed before formatting is skipped and a
-script is output verbatim.  The intention is to avoid accidentally formatting 
+script is output verbatim.  The intention is to avoid accidentally formatting
 a non-perl script, such as an html file for example.  This check can be turned
 off by setting B<n=0>.
 
 A recommended value is B<n=3>.  However, the default is B<n=0> (skip this check)
-to avoid causing problems with scripts which have extended syntaxes. 
+to avoid causing problems with scripts which have extended syntaxes.
 
-B<-DEBUG>  will write a file with extension F<.DEBUG> for each input file 
+B<-DEBUG>  will write a file with extension F<.DEBUG> for each input file
 showing the tokenization of all lines of code.
 
 =item B<Working with MakeMaker, AutoLoader and SelfLoader>
 
 The first $VERSION line of a file which might be eval'd by MakeMaker
-is passed through unchanged except for indentation.  
+is passed through unchanged except for indentation.
 Use B<--nopass-version-line>, or B<-npvl>, to deactivate this feature.
 
 If the AutoLoader module is used, perltidy will continue formatting
@@ -4387,7 +4750,7 @@ which may be viewed with a browser.
 B<Please Note>: In this case, perltidy does not do any formatting to the
 input file, and it does not write a formatted file with extension
 F<.tdy>.  This means that two perltidy runs are required to create a
-fully reformatted, html copy of a script.  
+fully reformatted, html copy of a script.
 
 =item  The B<-pre> flag for code snippets
 
@@ -4428,13 +4791,13 @@ the flags have a prefix C<pod> to emphasize that they are for the
 pod2html, and this prefix will be removed before they are passed to
 pod2html.  The flags which have the additional C<pod> prefix are:
 
-   --[no]podheader --[no]podindex --[no]podrecurse --[no]podquiet 
+   --[no]podheader --[no]podindex --[no]podrecurse --[no]podquiet
    --[no]podverbose --podflush
 
 The flags which are unchanged from their use in pod2html are:
 
    --backlink=s --cachedir=s --htmlroot=s --libpods=s --title=s
-   --podpath=s --podroot=s 
+   --podpath=s --podroot=s
 
 where 's' is an appropriate character string.  Not all of these flags are
 available in older versions of Pod::Html.  See your Pod::Html documentation for
@@ -4522,7 +4885,7 @@ the long form, B<-html-color-xxxxxx=n>, or more conveniently the short form,
 B<-hcx=n>, where B<xxxxxx> is one of the following words, and B<x> is the
 corresponding abbreviation:
 
-      Token Type             xxxxxx           x 
+      Token Type             xxxxxx           x
       ----------             --------         --
       comment                comment          c
       number                 numeric          n
@@ -4543,10 +4906,10 @@ corresponding abbreviation:
       pod text               pod-text         pd
 
 A default set of colors has been defined, but they may be changed by providing
-values to any of the following parameters, where B<n> is either a 6 digit 
+values to any of the following parameters, where B<n> is either a 6 digit
 hex RGB color value or an ascii name for a color, such as 'red'.
 
-To illustrate, the following command will produce an html 
+To illustrate, the following command will produce an html
 file F<somefile.pl.html> with "aqua" keywords:
 
        perltidy -html -hck=00ffff somefile.pl
@@ -4577,12 +4940,12 @@ The following 16 color names are defined in the HTML 3.2 standard:
 
 Many more names are supported in specific browsers, but it is safest
 to use the hex codes for other colors.  Helpful color tables can be
-located with an internet search for "HTML color tables". 
+located with an internet search for "HTML color tables".
 
 Besides color, two other character attributes may be set: bold, and italics.
 To set a token type to use bold, use the flag
 B<--html-bold-xxxxxx> or B<-hbx>, where B<xxxxxx> or B<x> are the long
-or short names from the above table.  Conversely, to set a token type to 
+or short names from the above table.  Conversely, to set a token type to
 NOT use bold, use B<--nohtml-bold-xxxxxx> or B<-nhbx>.
 
 Likewise, to set a token type to use an italic font, use the flag
@@ -4649,12 +5012,12 @@ flexibility, the following convention is used in all cases to decide if
 a leading '.' should be used.  If the extension C<ext> begins with
 C<A-Z>, C<a-z>, or C<0-9>, then it will be appended to the filename with
 an intermediate '.' (or perhaps a '_' on VMS systems).  Otherwise, it
-will be appended directly.  
+will be appended directly.
 
 For example, suppose the file is F<somefile.pl>.  For C<-bext=old>, a '.' is
 added to give F<somefile.pl.old>.  For C<-bext=.old>, no additional '.' is
 added, so again the backup file is F<somefile.pl.old>.  For C<-bext=~>, then no
-dot is added, and the backup file will be F<somefile.pl~>  .  
+dot is added, and the backup file will be F<somefile.pl~>  .
 
 =head1 SWITCHES WHICH MAY BE NEGATED
 
@@ -4665,19 +5028,19 @@ The following list shows all short parameter names which allow a prefix
  baao   bar    bbao   bbb    bbc    bbs    bl     bli    boa    boc
  bok    bol    bom    bos    bot    cblx   ce     conv   cs     csc
  cscb   cscw   dac    dbc    dcbl   dcsc   ddf    dln    dnl    dop
- dp     dpro   dsc    dsm    dsn    dtt    dwls   dwrs   dws    f
- fll    fpva   frm    fs     fso    gcs    hbc    hbcm   hbco   hbh
- hbhh   hbi    hbj    hbk    hbm    hbn    hbp    hbpd   hbpu   hbq
- hbs    hbsc   hbv    hbw    hent   hic    hicm   hico   hih    hihh
- hii    hij    hik    him    hin    hip    hipd   hipu   hiq    his
- hisc   hiv    hiw    hsc    html   ibc    icb    icp    iob    isbc
- iscl   kgb    kgbd   kgbi   kis    lal    log    lop    lp     lsl
- mem    nib    ohbr   okw    ola    olc    oll    olq    opr    opt
- osbc   osbr   otr    ple    pod    pvl    q      sac    sbc    sbl
- scbb   schb   scp    scsb   sct    se     sfp    sfs    skp    sob
- sobb   sohb   sop    sosb   sot    ssc    st     sts    t      tac
- tbc    toc    tp     tqw    trp    ts     tsc    tso    vmll   w
wn     x      xci    xs
+ dp     dpro   dsc    dsm    dsn    dtt    dwls   dwrs   dws    eos
+ f      fll    fpva   frm    fs     fso    gcs    hbc    hbcm   hbco
+ hbh    hbhh   hbi    hbj    hbk    hbm    hbn    hbp    hbpd   hbpu
+ hbq    hbs    hbsc   hbv    hbw    hent   hic    hicm   hico   hih
+ hihh   hii    hij    hik    him    hin    hip    hipd   hipu   hiq
+ his    hisc   hiv    hiw    hsc    html   ibc    icb    icp    iob
+ isbc   iscl   kgb    kgbd   kgbi   kis    lal    log    lop    lp
+ lsl    mem    nib    ohbr   okw    ola    olc    oll    olq    opr
+ opt    osbc   osbr   otr    ple    pod    pvl    q      sac    sbc
+ sbl    scbb   schb   scp    scsb   sct    se     sfp    sfs    skp
+ sob    sobb   sohb   sop    sosb   sot    ssc    st     sts    t
+ tac    tbc    toc    tp     tqw    trp    ts     tsc    tso    vbc
vc     vmll   vsc    w      wn     x      xci    xlp    xs
 
 Equivalently, the prefix 'no' or 'no-' on the corresponding long names may be
 used.
@@ -4703,7 +5066,7 @@ If you encounter a bug, please report it.
 
 =item  B<What perltidy does not parse and format>
 
-Perltidy indents but does not reformat comments and C<qw> quotes. 
+Perltidy indents but does not reformat comments and C<qw> quotes.
 Perltidy does not in any way modify the contents of here documents or
 quoted text, even if they contain source code.  (You could, however,
 reformat them separately).  Perltidy does not format 'format' sections
@@ -4726,7 +5089,7 @@ created in the current working directory.
 
 When standard input is used, the log file, if saved, is F<perltidy.LOG>,
 and any errors are written to F<perltidy.ERR> unless the B<-se> flag is
-set.  These are saved in the current working directory.  
+set.  These are saved in the current working directory.
 
 =item B<Files overwritten>
 
@@ -4751,7 +5114,7 @@ An exit value of 0, 1, or 2 is returned by perltidy to indicate the status of th
 
 A exit value of 0 indicates that perltidy ran to completion with no error messages.
 
-A non-zero exit value indicates some kind of problem was detected. 
+A non-zero exit value indicates some kind of problem was detected.
 
 An exit value of 1 indicates that perltidy terminated prematurely, usually due
 to some kind of errors in the input parameters.  This can happen for example if
@@ -4776,19 +5139,19 @@ The perltidy binary uses the Perl::Tidy module and is installed when that module
 
 =head1 VERSION
 
-This man page documents perltidy version 20210717
+This man page documents perltidy version 20220217
 
 =head1 BUG REPORTS
 
 A list of current bugs and issues can be found at the CPAN site L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perl-Tidy>
 
-To report a new bug or problem, use the link on this page.  
+To report a new bug or problem, use the link on this page.
 
 The source code repository is at L<https://github.com/perltidy/perltidy>.
 
 =head1 COPYRIGHT
 
-Copyright (c) 2000-2021 by Steve Hancock
+Copyright (c) 2000-2022 by Steve Hancock
 
 =head1 LICENSE
 
index 8af72619126b0a630b94d7818e8aa127624f71ab..75fa28c5dc5cee2e6192044dd25ccbb0142c1611 100644 (file)
@@ -12,6 +12,7 @@
 
 
 <ul id="index">
+  <li><a href="#Issues-fixed-after-release-20211029">Issues fixed after release 20211029</a></li>
   <li><a href="#Issues-fixed-after-release-20210625">Issues fixed after release 20210625</a></li>
   <li><a href="#Issues-fixed-after-release-20210402">Issues fixed after release 20210402</a></li>
   <li><a href="#Issues-fixed-after-release-20210111">Issues fixed after release 20210111</a></li>
   <li><a href="#Open-Issues">Open Issues</a></li>
 </ul>
 
+<h1 id="Issues-fixed-after-release-20211029">Issues fixed after release 20211029</h1>
+
+<dl>
+
+<dt id="Fix-tokenization-issue-c109"><b>Fix tokenization issue c109</b></dt>
+<dd>
+
+<p>Automated random testing produced an error tokenizing the following code fragment:</p>
+
+<pre><code>    s s(..)(.)sss
+    ;</code></pre>
+
+<p>This is equivalent to &#39;s/(..)(.)//s&#39; with &#39;s&#39; as the delimiter instead of &#39;/&#39;. It was tokenized correctly except when the final &#39;s&#39; was followed by a newline, as in the example. When the delimiter is a letter rather than a punctuation character, perltidy exercises some seldom-used code which had an off-by-one loop limit. This has been fixed.</p>
+
+<p>12 Nov 2021.</p>
+
+</dd>
+<dt id="Fix-tokenization-of-issue-c106"><b>Fix tokenization of $$^, issue c106</b></dt>
+<dd>
+
+<p>Automated random testing produced an error tokenizing the following fragment:</p>
+
+<pre><code>   my$seed=$$^$^T;</code></pre>
+
+<p>The first ^ should have been tokenized as the bitwise xor operator but was not. This is fixed with this update.</p>
+
+<p>8 Nov 2021</p>
+
+</dd>
+<dt id="Fix-coding-error-issue-c104"><b>Fix coding error, issue c104</b></dt>
+<dd>
+
+<p>Automated random testing produced an error with something like the following input line taken from an obfuscated perl script:</p>
+
+<pre><code>    open(IN, $ 0);</code></pre>
+
+<p>The &#39;0&#39; was missing in the output:</p>
+
+<pre><code>    open( IN, $ );</code></pre>
+
+<p>The tokenization was correct, but a line of code in the formatter which removes the space between the &#39;$&#39; and the &#39;0&#39; should have used a &#39;defined&#39; when doing a check:</p>
+
+<pre><code>    $token .= $word if ($word);             # OLD: error</code></pre>
+
+<p>This if test fails on &#39;0&#39;. The corrected line is</p>
+
+<pre><code>    $token .= $word if ( defined($word) );  # NEW: fixes c104</code></pre>
+
+<p>This fixes the problem and gives the correct formatted result</p>
+
+<pre><code>    open( IN, $0 );</code></pre>
+
+<p>8 Nov 2021.</p>
+
+</dd>
+<dt id="Fix-undefined-variable-reference-c102"><b>Fix undefined variable reference, c102</b></dt>
+<dd>
+
+<p>Random testing produced an undefined variable reference for the following input</p>
+
+<pre><code>    make_sorter ( sort_sha =&gt; sub {sha512 ( $_} );
+    make_sorter ( sort_ids =&gt; sub {/^ID:(\d+)/} );</code></pre>
+
+<p>when formatted with the following input parameters:</p>
+
+<pre><code>    --space-function-paren
+    --maximum-line-length=26
+    --noadd-newlines</code></pre>
+
+<p>Notice that the input script has a peculiar syntax error - the last two closing tokens of the first line are transposed. (Ironically, this snippet is taken from code which accompanied the book Perl Best Practices). The perltidy tokenizer caught the syntax error, but the formatter touched an undefined variable while attempting to do the formatting. It would be possible to just skip formatting for errors like this, but it can sometimes help finding bugs to see an attempted formatting. So the formatter coding has been corrected to avoid the undefined variable reference.</p>
+
+<p>This fixes issue c102.</p>
+
+<p>5 Nov 2021.</p>
+
+</dd>
+<dt id="Some-blocks-with-side-comments-exceed-line-length"><b>Some blocks with side comments exceed line length</b></dt>
+<dd>
+
+<p>In some rare cases, one-line blocks with side comments were exceeding the line length limit. These usually had a semicolon between the closing block brace and the side comment. For example:</p>
+
+<pre><code>        my $size
+            = do { local $^W; -f $local &amp;&amp; -s _ }; # no ALLO if sending data from a pipe</code></pre>
+
+<p>This update breaks the one-line block in an attempt to keep the total length below the line length limit. The result on the above is:</p>
+
+<pre><code>        my $size = do {
+            local $^W;
+            -f $local &amp;&amp; -s _;
+        };    # no ALLO if sending data from a pipe</code></pre>
+
+<p>Note that this break can be prevented by including the flag --ignore-side-comment-lengths or -iscl.</p>
+
+<p>3 Nov 2021.</p>
+
+</dd>
+</dl>
+
 <h1 id="Issues-fixed-after-release-20210625">Issues fixed after release 20210625</h1>
 
 <dl>
 
+<dt id="Fix-c090-inconsistent-warning-messages-for-deprecated-syntax"><b>Fix c090, inconsistent warning messages for deprecated syntax</b></dt>
+<dd>
+
+<p>For something like the following snippet, a warning about deprecated syntax was either going into the error file or the log file, depending on formatting. This has been fixed.</p>
+
+<pre><code>   do $roff ( &amp;verify($tpage) );</code></pre>
+
+<p>20 Oct 2021, 72e4bb1.</p>
+
+</dd>
+<dt id="Fix-c091-incorrect-closing-side-comment"><b>Fix c091, incorrect closing side comment</b></dt>
+<dd>
+
+<p>An error was discovered and corrected in the behavior of the --closing-side-comment (-csc) flag when only subs were being marked with the setting -cscl=&#39;sub&#39;. The problem was that in rare cases a closing paren could be marked with &#39;## end&#39;. The cause of the problem is that the pattern matching regex which was generated for this case happens to match an empty string, and it could happen that certain parens had empty strings as block names. This was fixed in two ways. First, the regex was fixed so that it cannot match an empty string. Second, a test for an empty string was added.</p>
+
+<p>20 Oct 2021, aa1a019.</p>
+
+</dd>
+<dt id="Issue-c089-improve-vertical-alignment-for-lists-without-parens"><b>Issue c089, improve vertical alignment for lists without parens</b></dt>
+<dd>
+
+<p>An update was made to improve vertical alignment in situations where parens are omitted around lists. The goal is to make lists without parens align as they would if they were contained in parens. Some examples:</p>
+
+<pre><code>    # OLD, no parens, no alignment:
+    glVertex3d $cx + $s * $xs, $cy, $z;
+    glVertex3d $cx, $cy + $s * $ys, $z;
+    glVertex3d $cx - $s * $xs, $cy, $z;
+    glVertex3d $cx, $cy - $s * $ys, $z;
+
+    # OLD, with parens and aligned:
+    glVertex3d( $cx + $s * $xs, $cy,            $z );
+    glVertex3d( $cx,            $cy + $s * $ys, $z );
+    glVertex3d( $cx - $s * $xs, $cy,            $z );
+    glVertex3d( $cx,            $cy - $s * $ys, $z );
+
+    # NEW, no parens but aligned
+    glVertex3d $cx + $s * $xs, $cy,            $z;
+    glVertex3d $cx,            $cy + $s * $ys, $z;
+    glVertex3d $cx - $s * $xs, $cy,            $z;
+    glVertex3d $cx,            $cy - $s * $ys, $z;
+
+    # OLD
+    mkTextConfig $c, $x, $y, -anchor =&gt; &#39;se&#39;, $color;
+    mkTextConfig $c, $x + 30, $y, -anchor =&gt; &#39;s&#39;,  $color;
+    mkTextConfig $c, $x + 60, $y, -anchor =&gt; &#39;sw&#39;, $color;
+    mkTextConfig $c, $x, $y + 30, -anchor =&gt; &#39;e&#39;, $color;
+
+    # NEW
+    mkTextConfig $c, $x,      $y,      -anchor =&gt; &#39;se&#39;, $color;
+    mkTextConfig $c, $x + 30, $y,      -anchor =&gt; &#39;s&#39;,  $color;
+    mkTextConfig $c, $x + 60, $y,      -anchor =&gt; &#39;sw&#39;, $color;
+    mkTextConfig $c, $x,      $y + 30, -anchor =&gt; &#39;e&#39;,  $color;
+
+    # OLD
+    permute_test [ &#39;a&#39;, &#39;b&#39;, &#39;c&#39; ],   &#39;/&#39;, &#39;/&#39;, [ &#39;a&#39;, &#39;b&#39;, &#39;c&#39; ];
+    permute_test [ &#39;a,&#39;, &#39;b&#39;, &#39;c,&#39; ], &#39;/&#39;, &#39;/&#39;, [ &#39;a,&#39;, &#39;b&#39;, &#39;c,&#39; ];
+    permute_test [ &#39;a&#39;, &#39;,&#39;, &#39;#&#39;, &#39;c&#39; ], &#39;/&#39;, &#39;/&#39;, [ &#39;a&#39;, &#39;,&#39;, &#39;#&#39;, &#39;c&#39; ];
+    permute_test [ &#39;f_oo&#39;, &#39;b_ar&#39; ], &#39;/&#39;, &#39;/&#39;, [ &#39;f_oo&#39;, &#39;b_ar&#39; ];
+
+    # NEW
+    permute_test [ &#39;a&#39;, &#39;b&#39;, &#39;c&#39; ],      &#39;/&#39;, &#39;/&#39;, [ &#39;a&#39;, &#39;b&#39;, &#39;c&#39; ];
+    permute_test [ &#39;a,&#39;, &#39;b&#39;, &#39;c,&#39; ],    &#39;/&#39;, &#39;/&#39;, [ &#39;a,&#39;, &#39;b&#39;, &#39;c,&#39; ];
+    permute_test [ &#39;a&#39;, &#39;,&#39;, &#39;#&#39;, &#39;c&#39; ], &#39;/&#39;, &#39;/&#39;, [ &#39;a&#39;, &#39;,&#39;, &#39;#&#39;, &#39;c&#39; ];
+    permute_test [ &#39;f_oo&#39;, &#39;b_ar&#39; ],     &#39;/&#39;, &#39;/&#39;, [ &#39;f_oo&#39;, &#39;b_ar&#39; ];
+
+    # OLD:
+    is $thingy, &quot;fee&quot;,           &quot;source filters apply to evalbytten strings&quot;;
+    is &quot;foo&quot;,   $unfiltered_foo, &#39;filters leak not out of byte evals&#39;;
+    is $av-&gt;[2], &quot;NAME:my_xop&quot;,          &quot;OP_NAME returns registered name&quot;;
+    is $av-&gt;[3], &quot;DESC:XOP for testing&quot;, &quot;OP_DESC returns registered desc&quot;;
+    is $av-&gt;[4], &quot;CLASS:$OA_UNOP&quot;,       &quot;OP_CLASS returns registered class&quot;;
+    is scalar @$av, 7, &quot;registered peep called&quot;;
+    is $av-&gt;[5], &quot;peep:$unop&quot;, &quot;...with correct &#39;o&#39; param&quot;;
+    is $av-&gt;[6], &quot;oldop:$kid&quot;, &quot;...and correct &#39;oldop&#39; param&quot;;
+
+    # NEW
+    is $av-&gt;[2],    &quot;NAME:my_xop&quot;,          &quot;OP_NAME returns registered name&quot;;
+    is $av-&gt;[3],    &quot;DESC:XOP for testing&quot;, &quot;OP_DESC returns registered desc&quot;;
+    is $av-&gt;[4],    &quot;CLASS:$OA_UNOP&quot;,       &quot;OP_CLASS returns registered class&quot;;
+    is scalar @$av, 7,                      &quot;registered peep called&quot;;
+    is $av-&gt;[5],    &quot;peep:$unop&quot;,           &quot;...with correct &#39;o&#39; param&quot;;
+    is $av-&gt;[6],    &quot;oldop:$kid&quot;,           &quot;...and correct &#39;oldop&#39; param&quot;;</code></pre>
+
+<p>20 Oct 2021, 1dffec5.</p>
+
+</dd>
+<dt id="Issue-c087-breaking-after-anonymous-sub"><b>Issue c087, breaking after anonymous sub</b></dt>
+<dd>
+
+<p>This update keeps both of these configurations stable for all cases except when the -lp option is used. For the -lp option, both become one-line blocks (the second case) to prevents the -lp indentation style from being lost. This update was made to minimize changes to existing formatting.</p>
+
+<pre><code>    $obj = {
+        foo =&gt; sub { &quot;bar&quot; }
+    };
+
+    $obj = { foo =&gt; sub { &quot;bar&quot; } };</code></pre>
+
+<p>17 Oct 2021, f05e6b5.</p>
+
+</dd>
+<dt id="Improve-formatting-of-some-Moose-structures"><b>Improve formatting of some Moose structures</b></dt>
+<dd>
+
+<p>In some structures used in Moose coding, some asymmetrical container breaks were being caused by the braces being tokenized as hash braces rather than block braces. This was also causing some unwanted warning messages.</p>
+
+<pre><code>    # OLD
+    ::is(
+        ::exception { has &#39;+bar&#39; =&gt; ( default =&gt; sub { 100 } );
+        },
+        undef,
+        &#39;... extended the attribute successfully&#39;
+    );
+
+    # NEW
+    ::is(
+        ::exception {
+            has &#39;+bar&#39; =&gt; ( default =&gt; sub { 100 } );
+        },
+        undef,
+        &#39;... extended the attribute successfully&#39;
+    );</code></pre>
+
+<p>This fixes issue c074.</p>
+
+<p>12 Oct 2021, 7e873fa.</p>
+
+</dd>
+<dt id="Fix-issue-c081--cscw-preventing-deletion-of-closing-side-comments"><b>Fix issue c081, -cscw preventing deletion of closing side comments</b></dt>
+<dd>
+
+<p>Random testing revealed a problem in which an old closing side comment was not being deleted when it fell below the interval specified on -csci=n and the -cscw flag was also set.</p>
+
+<p>For example, the following snippet has been formatted with -csc -csci=1. The interval -csci=1 insures that all blocks get side comments:</p>
+
+<pre><code>    if ($x3) {
+        $zz;
+        if ($x2) {
+            if ($x1) {
+                ..;
+            } ## end if ($x1)
+            $a;
+            $b;
+            $c;
+        } ## end if ($x2)
+    } ## end if ($x3)</code></pre>
+
+<p>If we then run with -csc -csci=6, the comment ## end if ($x1) will fall below the threshold and be removed (correctly):</p>
+
+<pre><code>    if ($x3) {
+        $zz;
+        if ($x2) {
+            if ($x1) {
+                ..;
+            }
+            $a;
+            $b;
+            $c;
+        } ## end if ($x2)
+    } ## end if ($x3)</code></pre>
+
+<p>But if we also add the -cscw flag (warnings) then it was not being removed. This update fixes this problem (issue c081).</p>
+
+<p>2 Oct 2021, 25ef8e8</p>
+
+</dd>
+<dt id="Partial-fix-for-issue-git-74-on--lp-at-anonymous-subs"><b>Partial fix for issue git #74 on -lp at anonymous subs</b></dt>
+<dd>
+
+<p>In the following snippet, the final one-line anonymous sub is not followed by a comma. This caused the -lp mode to revert to standard indentation mode because a forced line break was being made after the closing sub brace:</p>
+
+<pre><code>    # OLD, perltidy -lp
+    $got = timethese(
+        $iterations,
+        {
+          Foo =&gt; sub { ++$foo },
+          Bar =&gt; &#39;++$bar&#39;,
+          Baz =&gt; sub { ++$baz }
+        }
+    );</code></pre>
+
+<p>An update was made to check for and fix this.</p>
+
+<pre><code>    # NEW, perltidy -lp
+    $got = timethese(
+                      $iterations,
+                      {
+                         Foo =&gt; sub { ++$foo },
+                         Bar =&gt; &#39;++$bar&#39;,
+                         Baz =&gt; sub { ++$baz }
+                      }
+    );</code></pre>
+
+<p>But note that this only applies to one-line anonymous subs. If an anonymous sub is broken into several lines due to its length or complexity, then these forced line breaks cause indentation to revert to the standard indentation scheme.</p>
+
+<p>22 Sep 2021, 4fd58f7.</p>
+
+</dd>
+<dt id="Fix-issues-b1209-related-to--vmll"><b>Fix issues b1209 related to -vmll</b></dt>
+<dd>
+
+<p>Testing with random parameters produced a formatting instability related to the -vmll flag. The problem was due to a subtle difference in the definition of nesting depth and indentation level. The vertical aligner was using nesting depth instead of indentation level to compute the maximum line length when -vmll is set. In some rare cases there is a difference. The problem was fixed by passing the maximum line length to the vertical aligner so that the calculation is only done in the formatter. This fixes b1209.</p>
+
+<p>20 Sep 2021, acf1d2d.</p>
+
+</dd>
+<dt id="Fix-issue-b1208"><b>Fix issue b1208</b></dt>
+<dd>
+
+<p>Testing with random parameters produced a formatting instability which could be triggered when there is a short line length limit and there is a long side comment on the closing brace of a sort/map/grep/eval block. The problem was due to not correctly including the length of the side comment when testing to see if the block could fit on one line. This update fixes issue b1208.</p>
+
+<p>18 Sep 2021, 0af1321.</p>
+
+</dd>
+<dt id="Fix-issue-git-73"><b>Fix issue git #73</b></dt>
+<dd>
+
+<p>The -nfpva parameter was not working correctly for functions called with pointers. For example</p>
+
+<pre><code>    # OLD: perltidy -sfp -nfpva
+    $self-&gt;method                ( &#39;parameter_0&#39;, &#39;parameter_1&#39; );
+    $self-&gt;method_with_long_name ( &#39;parameter_0&#39;, &#39;parameter_1&#39; );
+
+    # FIXED: perltidy -sfp -nfpva
+    $self-&gt;method ( &#39;parameter_0&#39;, &#39;parameter_1&#39; );
+    $self-&gt;method_with_long_name ( &#39;parameter_0&#39;, &#39;parameter_1&#39; );</code></pre>
+
+<p>The problem had to do with how the pointer tokens &#39;-&gt;&#39; are represented internally and has been fixed.</p>
+
+<p>17 Sep 2021,e3b4a6f.</p>
+
+</dd>
+<dt id="Fix-unusual-parsing-error-b1207"><b>Fix unusual parsing error b1207</b></dt>
+<dd>
+
+<p>Testing with random parameters produced another instability caused by misparsing an &#39;x&#39; operator after a possible file handle. This is very similar to b1205, but involves a sigil immediately following a times operator.</p>
+
+<p>To illustrate some cases, consider:</p>
+
+<pre><code>    sub x {print &quot;arg is $_[0]\n&quot;}
+    my $num = 3;
+    my @list=(1,2,3);
+    my %hash=(1,2,3,4);
+    open (my $fh, &quot;&gt;&quot;, &quot;junk.txt&quot;);
+    print $fh x$num;   # prints a GLOB $num times to STDOUT
+    print $fh x9;      # prints &#39;x9&#39; to file &#39;junk.txt&#39;
+    print $fh x@list;  # prints a GLOB 3 times to STDOUT
+    print $fh x%hash;  # prints a GLOB 2 times to STDOUT</code></pre>
+
+<p>Note in particular the different treatment of the first two print statements.</p>
+
+<p>This update fixes case b1207.</p>
+
+<p>15 Sep 2021, 107586f.</p>
+
+</dd>
+<dt id="Fix-formatting-instability-b1206"><b>Fix formatting instability b1206</b></dt>
+<dd>
+
+<p>Testing with random parameters produced an instability due welding with a very short line length and large value of -ci. This is similar to issues b1197-b1204 and fixed with a similar method.</p>
+
+<p>14 Sep 2021, 9704cd7.</p>
+
+</dd>
+<dt id="Fix-unusual-parsing-error-b1205"><b>Fix unusual parsing error b1205</b></dt>
+<dd>
+
+<p>Testing with random parameters produced an instability caused by misparsing an &#39;x&#39; operator after a possible file handle. Testing with Perl showed that an &#39;x&#39; followed by a &#39;(&#39; in this location is always the &#39;times&#39; operator and never a call to a function &#39;x&#39;. If x is immediately followed by a number it is subject to the usual weird parsing rules at such a location.</p>
+
+<p>To illustrate, consider what these statements do:</p>
+
+<pre><code>    open( my $zz, &quot;&gt;&quot;, &quot;junk.txt&quot; );
+    sub x { return $_[0] }  # never called
+    print $zz x(2);    # prints a glob 2 times; not a function call
+    print $zz x 2;     # prints a glob 2 times
+    print $zz x2;      # syntax error
+    print $zz x;       # syntax error
+    print $zz z;       # prints &#39;z&#39; in file &#39;junk.txt&#39;</code></pre>
+
+<p>This update fixes case b1205.</p>
+
+<p>13 Sep 2021, cfa2515.</p>
+
+</dd>
+<dt id="Use-stress_level-to-fix-cases-b1197-b1204"><b>Use stress_level to fix cases b1197-b1204</b></dt>
+<dd>
+
+<p>Testing with random input parameters produced a number of cases of unstable formatting. All of these involved some combination of a short maximum line length, a large -ci and -i, and often one or more of -xci -lp and -wn. These parameters can force lines to break at poor locations. All of these cases were fixed by introducing a quantity called the &#39;stress_level&#39;, which is the approximate indentation level at which the line break logic comes under high stress and become unstable. For default parameters the stress level is about 12, but unusual parameter combinations can make it much less, even as low as zero. For code which is at an indentation level greater than this depth, some defensive actions are taken to avoid instability, such as temporarily turning off the -xci flag when the indentation depth exceeds the stress level. Most actual working code will not be influenced by this logic. Actual code which has a very deep indentation level can avoid problems by using a long line length, a short number of indentation spaces, or even the whitespace-cycle parameter.</p>
+
+<p>This update fixes issues b1197 b1198 b1199 b1200 b1201 b1202 b1203 b1204</p>
+
+<p>12 Sep 2021, 0ac771e.</p>
+
+</dd>
+<dt id="Fix-unusual-hanging-side-comment-issue-c070"><b>Fix unusual hanging side comment issue, c070</b></dt>
+<dd>
+
+<p>This issues can be illustrated with the following input script:</p>
+
+<pre><code>    {
+        if ($xxx) {
+          ...
+        } ## end if ($xxx ...
+        # b &lt;filename&gt;:&lt;line&gt; [&lt;condition&gt;]
+    }
+
+
+    # OLD: perltidy -fpsc=21
+    {
+        if ($xxx) {
+            ...;
+        } ## end if ($xxx ...
+                        # b &lt;filename&gt;:&lt;line&gt; [&lt;condition&gt;]
+    }</code></pre>
+
+<p>The comment &#39;# b ..&#39; moved over to the column 21 to the right as if it were a side comment. The reason is that it accidentally got marked as a hanging side comment. It should not have been because the previous side comment was a closing side comment. This update fixes this:</p>
+
+<pre><code>    # NEW: perltidy -fpsc=21
+    {
+        if ($xxx) {
+            ...;
+        } ## end if ($xxx ...
+
+        # b &lt;filename&gt;:&lt;line&gt; [&lt;condition&gt;]
+    }</code></pre>
+
+<p>This fixes issue c070.</p>
+
+<p>10 Sep 2021, ec6ccf9.</p>
+
+</dd>
+<dt id="Fix-parsing-issue-c068"><b>Fix parsing issue c068</b></dt>
+<dd>
+
+<p>This issue is illustrated with the following line:</p>
+
+<pre><code>   my $aa = $^ ? &quot;defined&quot; : &quot;not defined&quot;;</code></pre>
+
+<p>If we tell perltidy to remove the space before the &#39;?&#39;, then the output will no longer be a valid script:</p>
+
+<pre><code>   # perltidy -nwls=&#39;?&#39;:
+   my $aa = $^? &quot;defined&quot; : &quot;not defined&quot;;</code></pre>
+
+<p>The problem is that Perl considers &#39;$^?&#39; to be a special variable. So Rerunning perltidy on this gives an error, and perl also gives an error. This update fixes the problem by preventing a space after anything like &#39;$^&#39; from being removed a new special variable would be created.</p>
+
+<p>This fixes issue c068.</p>
+
+<p>7 Sep 2021, 9bc23d1.</p>
+
+</dd>
+<dt id="Fix-parsing-problem-issue-c066"><b>Fix parsing problem issue c066</b></dt>
+<dd>
+
+<p>This issue is illustrated with the following line (rt80058):</p>
+
+<pre><code>   my $ok=$^Oeq&quot;linux&quot;;</code></pre>
+
+<p>Running perltidy generated a warning message which is caused by the lack of space before the &#39;eq&#39;. This update fixes the problem.</p>
+
+<p>4 Sep 2021, df79a20.</p>
+
+</dd>
+<dt id="Fix-unusual-parsing-problem-issue-c065"><b>Fix unusual parsing problem issue c065</b></dt>
+<dd>
+
+<p>Testing produced an unusual parsing problem in perltidy which can be illustrated with the following simple script:</p>
+
+<pre><code>    my $str = &#39;a&#39;x2.2;
+    print $str,&quot;\n&quot;;</code></pre>
+
+<p>Normally an integer would follow the &#39;x&#39; operator, but Perl seems to accept any valid number and truncates it to an integer. So in this case the number 2.2 is truncated to 2 and the output is &#39;aa&#39;.</p>
+
+<p>But perltidy, with default parameters, formats this as</p>
+
+<pre><code>    my $str = &#39;a&#39; x 2 . 2;
+    print $str,&quot;\n&quot;;</code></pre>
+
+<p>which prints the result &quot;aa2&quot;. The problem is fixed with this update. With the update, the result is</p>
+
+<pre><code>    my $str = &#39;a&#39; x 2.2;
+    print $str, &quot;\n&quot;;</code></pre>
+
+<p>which is equivalent to the original script.</p>
+
+<p>This fixes issue c065.</p>
+
+<p>4 Sep 2021, f242f78.</p>
+
+</dd>
+<dt id="Fix-formatting-instability-issues-b1195-b1196"><b>Fix formatting instability issues b1195, b1196</b></dt>
+<dd>
+
+<p>Testing with random parameters produced two similar cases of unstable formatting which are fixed with this update.</p>
+
+<p>28 Aug 2021, ab9ad39.</p>
+
+</dd>
+<dt id="Fix-formatting-instability-issue-b1194"><b>Fix formatting instability issue b1194</b></dt>
+<dd>
+
+<p>Testing with random parameters produced a case of unstable formatting which is fixed with this update.</p>
+
+<p>This fixes case b1194, and at the same time it simplifies the logic which handles issues b1183 and b1191.</p>
+
+<p>21 Aug 2021, 62e5b01.</p>
+
+</dd>
+<dt id="Fix-some-problems-involving-tabs-characters-case-c062"><b>Fix some problems involving tabs characters, case c062</b></dt>
+<dd>
+
+<p>This update fixes some problems found in random testing with tab characters. For example, in the following snippet there is a tab character after &#39;sub&#39;</p>
+
+<pre><code>    do sub      : lvalue {
+        return;
+      }</code></pre>
+
+<p>Running perltidy on this repeatedly keep increasing the space between &#39;sub&#39; and &#39;:&#39;</p>
+
+<pre><code>    # OLD: perltidy
+    do sub       : lvalue {
+        return;
+      }
+
+    # OLD: perltidy
+    do sub        : lvalue {
+        return;
+      }</code></pre>
+
+<p>etc.</p>
+
+<pre><code>    # NEW: perltidy
+    do sub : lvalue {
+        return;
+      }</code></pre>
+
+<p>Problems like this can occur if string comparisons use &#39; &#39; instead of the regex \s when working on spaces. Several instances of this were located and corrected.</p>
+
+<p>This fixes issue c062.</p>
+
+<p>18 Aug 2021, d86787f.</p>
+
+</dd>
+<dt id="Correct-parsing-error-case-c061"><b>Correct parsing error, case c061</b></dt>
+<dd>
+
+<p>Testing with random input produced an error condition involving a side comment placed between a sub name and prototype, as in the following snippet:</p>
+
+<pre><code>    sub
+    witch   # sc
+    ()   # prototype may be on new line ...
+    { print &quot;which?\n&quot; }
+    witch();</code></pre>
+
+<p>The current version of perltidy makes an error:</p>
+
+<pre><code>    # OLD: perltidy
+    sub witch   # sc ()    # prototype may be on new line ...
+    { print &quot;which?\n&quot; }
+    witch();</code></pre>
+
+<p>This update corrects the problem:</p>
+
+<pre><code>    # NEW: perltidy
+    sub witch    # sc
+      ()         # prototype may be on new line ...
+    { print &quot;which?\n&quot; }
+    witch();</code></pre>
+
+<p>This fixes case c061;</p>
+
+<p>18 Aug 2021, 3bb2b2c.</p>
+
+</dd>
+<dt id="Improved-line-break-case-c060"><b>Improved line break, case c060</b></dt>
+<dd>
+
+<p>The default formatting produced an undesirable line break at the last &#39;&amp;&amp;&#39; term in the following:</p>
+
+<pre><code>    my $static = grep {
+             $class       =~ /^$_$/
+          || $fullname    =~ /^$_$/
+          || $method_name =~ /^$_$/
+          &amp;&amp; ( $class eq &#39;main&#39; )
+    } grep { !m![/\\.]! } $self-&gt;dispatch_to;    # filter PATH</code></pre>
+
+<p>This update corrects this to give</p>
+
+<pre><code>    my $static = grep {
+             $class       =~ /^$_$/
+          || $fullname    =~ /^$_$/
+          || $method_name =~ /^$_$/ &amp;&amp; ( $class eq &#39;main&#39; )
+    } grep { !m![/\\.]! } $self-&gt;dispatch_to;    # filter PATH</code></pre>
+
+<p>15 Aug 2021, 9d1c8a9.</p>
+
+</dd>
+<dt id="Fix-error-check-caused-by--wn--iscl-case-c058"><b>Fix error check caused by -wn -iscl, case c058</b></dt>
+<dd>
+
+<p>Testing with random parameters triggered an an internal error check. This was caused by a recent coding change which allowed a weld across a side comment. The problem was in the development version, not in the latest released version, and is fixed with this update. This closes issue c058.</p>
+
+<p>14 Aug 2021, 5a13886.</p>
+
+</dd>
+<dt id="Fix-formatting-instability-b1193"><b>Fix formatting instability, b1193</b></dt>
+<dd>
+
+<p>Testing with random parameters produced unstable formatting involving parameters which included -lp -sbvtc=1. This update fixes this problem, case b1193.</p>
+
+<p>13 Aug 2021, d4c3425.</p>
+
+</dd>
+<dt id="Fix-error-in-tokenizer-issue-c055"><b>Fix error in tokenizer, issue c055</b></dt>
+<dd>
+
+<p>The ultimate cause of the undefined variable reference in the previous issue was found to be a typo in the tokenizer. This update finishes fixing issue c055.</p>
+
+<p>10 Aug 2021, 2963db3</p>
+
+</dd>
+<dt id="Fix-undefined-variable-reference-in-development-version"><b>Fix undefined variable reference in development version</b></dt>
+<dd>
+
+<p>In testing, the following unbalanced snippet caused a reference to an undefined value in the current development version (but not in the current released version).</p>
+
+<pre><code>        if($CPAN::Config-&gt;{term_is_latin}){
+                $swhat=~s{([\xC0-\xDF])([\x80-\xBF])}{chr(ord($1)&lt;&lt;6&amp;0xC0|ord($2)&amp;0x3F)}eg;}if($self-&gt;colorize_output){if($CPAN::DEBUG&amp;&amp;$swhat=~/^Debug\(/){
+                        $ornament=$CPAN::Config-&gt;{colorize_debug}||&quot;black on_cyan&quot;;}</code></pre>
+
+<p>A check has been added to fix this.</p>
+
+<p>10 Aug 2021, a3f9774.</p>
+
+</dd>
+<dt id="Fix-formatting-instability-b1192"><b>Fix formatting instability, b1192</b></dt>
+<dd>
+
+<p>Testing with random parameters produced unstable formatting with the following snippet when run with some unusual parameters:</p>
+
+<pre><code>          @EXPORT =
+              ( @{$EXPORT_TAGS{standard}}, );</code></pre>
+
+<p>It was also be formatted as</p>
+
+<pre><code>          @EXPORT = (
+                      @{$EXPORT_TAGS{standard}},
+          );</code></pre>
+
+<p>The problem was that a list formatting style was turning on and off due to the the needless terminal comma within the parens. A patch was made to ignore commas like this when deciding if list formatting should be used.</p>
+
+<p>This fixes case b1192.</p>
+
+<p>10 Aug 2021, b949215.</p>
+
+</dd>
+<dt id="Fix-formatting-instability-b1191"><b>Fix formatting instability, b1191</b></dt>
+<dd>
+
+<p>Random testing produced an instability involving an unusual parameter combination and the following input code:</p>
+
+<pre><code>    $_[0]eq$_[1]
+      or($_[1]=~/^([!~])(.)([\x00-\xff]*)/)
+      and($1 eq &#39;!&#39;)
+      ^(eval{($_[2].&quot;::&quot;.$_[0])=~/$2$3/;});</code></pre>
+
+<p>This update fixes case b1191.</p>
+
+<p>9 Aug 2021, 16b4575.</p>
+
+</dd>
+<dt id="Fix-error-parsing-sub-attributes-without-spaces-b1190"><b>Fix error parsing sub attributes without spaces, b1190</b></dt>
+<dd>
+
+<p>Testing with random parameters produced an instability which was caused by incorrect parsing of a sub attribute list without spaces, as in</p>
+
+<pre><code>        sub:lvalue{&quot;a&quot;}</code></pre>
+
+<p>This update fixes case b1190.</p>
+
+<p>9 Aug 2021, 7008bcc.</p>
+
+</dd>
+<dt id="Fix-rare-loss-of-vertical-alignment-in-welded-containers-c053"><b>Fix rare loss of vertical alignment in welded containers, c053</b></dt>
+<dd>
+
+<p>This update corrects a rare loss of vertical alignment in welded containers.</p>
+
+<p>To illustrate the issue, the normal formatting of the following snippet is</p>
+
+<pre><code>    # perltidy -sil=1 
+    ( $msg, $defstyle ) = do {
+            $i == 1 ? ( &quot;First&quot;, &quot;Color&quot; )
+          : $i == 2 ? ( &quot;Then&quot;, &quot;Rarity&quot; )
+          :           ( &quot;Then&quot;, &quot;Name&quot; );
+    };</code></pre>
+
+<p>If it appears within a welded container, the alignment of the last line was being lost:</p>
+
+<pre><code>    # OLD: perltidy -wn -sil=1
+    { {
+
+        ( $msg, $defstyle ) = do {
+                $i == 1 ? ( &quot;First&quot;, &quot;Color&quot; )
+              : $i == 2 ? ( &quot;Then&quot;,  &quot;Rarity&quot; )
+              : ( &quot;Then&quot;, &quot;Name&quot; );
+        };
+    } }</code></pre>
+
+<p>The corrected result is</p>
+
+<pre><code>    # NEW: perltidy -wn -sil=1
+    { {
+
+        ( $msg, $defstyle ) = do {
+                $i == 1 ? ( &quot;First&quot;, &quot;Color&quot; )
+              : $i == 2 ? ( &quot;Then&quot;, &quot;Rarity&quot; )
+              :           ( &quot;Then&quot;, &quot;Name&quot; );
+        };
+    } }</code></pre>
+
+<p>Several other minor vertical alignment issues are fixed with this updated. The problem was that two slightly different measures of the depth of indentation were being compared in the vertical aligner.</p>
+
+<p>This fixes case c053.</p>
+
+<p>8 Aug 2021, 97f02ee.</p>
+
+</dd>
+<dt id="Fix-edge-case-of-formatting-instability-b1189"><b>Fix edge case of formatting instability, b1189</b>.</dt>
+<dd>
+
+<p>Testing with random parameters produced a case of unstable formatting involving welding with parameter -notrim-qw. The problem was that the -notrim-qw flag converts a qw quote into a quote with fixed leading whitespace. The lines of these types of quotes which have no other code are are output early in the formatting process, since they have a fixed format, so welding does not work. In particular, the closing tokens cannot be welded if they are on a separate line. This also holds for all types of non-qw quotes. So welding can only be done if the first and last lines of a non-qw quote contain other code. A check for this was added.</p>
+
+<p>For example, in the following snippet the terminal &#39;}&#39; is alone on a line:</p>
+
+<pre><code>    is eval(q{
+        $[ = 3;
+        BEGIN { my $x = &quot;foo\x{666}&quot;; $x =~ /foo\p{Alnum}/; }
+        $t[3];
+    }
+    ), &quot;a&quot;;</code></pre>
+
+<p># In the previous version this was half-welded: # OLD: perltidy -wn -sil=0</p>
+
+<pre><code>    is eval( q{
+        $[ = 3;
+        BEGIN { my $x = &quot;foo\x{666}&quot;; $x =~ /foo\p{Alnum}/; }
+        $t[3];
+    }
+      ),
+      &quot;a&quot;;</code></pre>
+
+<p>The new check avoids welding in this case, giving</p>
+
+<pre><code>    # NEW: perltidy -wn -sil=0
+    is eval(
+        q{
+        $[ = 3;
+        BEGIN { my $x = &quot;foo\x{666}&quot;; $x =~ /foo\p{Alnum}/; }
+        $t[3];
+    }
+      ),
+      &quot;a&quot;;</code></pre>
+
+<p>Welding can still be done if the opening and closing container tokens have other code. For example, welding can be done for the following snippet:</p>
+
+<pre><code>    is eval(q{
+        $[ = 3;
+        BEGIN { my $x = &quot;foo\x{666}&quot;; $x =~ /foo\p{Alnum}/; }
+        $t[3];
+    }), &quot;a&quot;;</code></pre>
+
+<p>And welding can still be done on all qw quotes unless the -notrim-qw flag is set.</p>
+
+<p>This fixes case b1189.</p>
+
+<p>7 Aug 2021, e9c25f2.</p>
+
+</dd>
+<dt id="Fix-edge-cases-of-formatting-instability-b1187-b1188"><b>Fix edge cases of formatting instability, b1187 b1188</b>.</dt>
+<dd>
+
+<p>Testing with random parameters produced some cases of instability involving -wn -lp and several other parameters. The mechanism involved an interaction between the formatter and vertical aligner.</p>
+
+<p>This fixes cases b1187 and b1188.</p>
+
+<p>3 Aug 2021, 5be949b.</p>
+
+</dd>
+<dt id="Fix-edge-case-of-formatting-instability-b1186"><b>Fix edge case of formatting instability, b1186</b>.</dt>
+<dd>
+
+<p>Testing with random parameters produced a case of instability involving parameter -lp -pvt=n and a short maximum line length.</p>
+
+<p>This fixes case b1186.</p>
+
+<p>2 Aug 2021, f3dbee1.</p>
+
+</dd>
+<dt id="Fix-edge-case-of-formatting-instability-b1185"><b>Fix edge case of formatting instability, b1185</b>.</dt>
+<dd>
+
+<p>Testing with random parameters produced a case of welding instability involving parameters -wn, -vt=2, -lp and a short maximum line length.</p>
+
+<p>This fixes case b1185.</p>
+
+<p>1 Aug 2021, d2ab2b7.</p>
+
+</dd>
+<dt id="Fix-edge-case-of-formatting-instability-b1183"><b>Fix edge case of formatting instability, b1183</b>.</dt>
+<dd>
+
+<p>Testing with random parameters produced a case of welding instability involving parameters -wn, -vt=n, -lp. This update fixes the problem.</p>
+
+<p>This fixes case b1183.</p>
+
+<p>30 Jul 2021, 055650b.</p>
+
+</dd>
+<dt id="Fix-edge-case-of-formatting-instability-b1184"><b>Fix edge case of formatting instability, b1184</b>.</dt>
+<dd>
+
+<p>Testing with random parameters produced a case of welding instability involving a tripple weld with parameters -wn, -vt=n, -lp. This update fixes the problem.</p>
+
+<p>This fixes case b1184.</p>
+
+<p>29 Jul 2021, 6dd53fb.</p>
+
+</dd>
+<dt id="Fix-edge-case-of-formatting-instability-b1182"><b>Fix edge case of formatting instability, b1182</b>.</dt>
+<dd>
+
+<p>Testing with random parameters produced a case of welding instability involving unusual parameters and welding long ternary expressions. This update fixes the problem.</p>
+
+<p>This fixes case b1182.</p>
+
+<p>28 Jul 2021, 01d6c40.</p>
+
+</dd>
+<dt id="Fix-edge-case-of-formatting-instability"><b>Fix edge case of formatting instability</b>.</dt>
+<dd>
+
+<p>Random testing with random input parameters produced cases of formatting instability involving welding with unusual parameter settings. This update makes a small tolarance adjustment to fix it.</p>
+
+<p>This fixes cases b1180 b1181.</p>
+
+<p>28 Jul 2021, b38ccfc.</p>
+
+</dd>
+<dt id="Fix-rare-problem-with-formatting-nested-ternary-statements"><b>Fix rare problem with formatting nested ternary statements</b></dt>
+<dd>
+
+<p>This update fixes an extremely rare problem in formatting nested ternary statements, illustrated in the following snippet:</p>
+
+<pre><code>  # OLD: There should be a break before the &#39;?&#39; in line 11 here:
+  WriteMakefile(
+      (
+          $PERL_CORE ? ()
+          : (
+              (
+                  eval { ExtUtils::MakeMaker-&gt;VERSION(6.48) }
+                  ? ( MIN_PERL_VERSION =&gt; &#39;5.006&#39; )
+                  : ()
+              ),
+              (
+                  eval { ExtUtils::MakeMaker-&gt;VERSION(6.46) } ? (
+                      META_MERGE =&gt; {
+                          #
+                      }
+                    )
+                  : ()
+              ),
+          )
+      ),
+  );
+
+  # NEW: Line 12 correctly begins with a &#39;?&#39;
+  WriteMakefile(
+      (
+          $PERL_CORE ? ()
+          : (
+              (
+                  eval { ExtUtils::MakeMaker-&gt;VERSION(6.48) }
+                  ? ( MIN_PERL_VERSION =&gt; &#39;5.006&#39; )
+                  : ()
+              ),
+              (
+                  eval { ExtUtils::MakeMaker-&gt;VERSION(6.46) }
+                  ? (
+                      META_MERGE =&gt; {
+                          #
+                      }
+                    )
+                  : ()
+              ),
+          )
+      ),
+  );</code></pre>
+
+<p>This fixes issue c050, 63784d8.</p>
+
+<p>22 Jul 2021.</p>
+
+</dd>
+<dt id="Fix-conflict-of--bom-and--scp-parameters"><b>Fix conflict of -bom and -scp parameters</b></dt>
+<dd>
+
+<p>Automated testing with random parameters produced a case of instability caused by a conflict of parameters -bom and -scp. In the following script the -bom command says to keep the tokens &#39;)-&gt;&#39; on a new line, whereas the -scp command says to stack the closing paren on the previous line.</p>
+
+<pre><code>        $resource = {
+                id =&gt; $package-&gt;new_from_mana(
+                        $result-&gt;{data}
+                )-&gt;id
+        };</code></pre>
+
+<p>The parameters are:</p>
+
+<pre><code>    --break-at-old-method-breakpoints
+    --indent-columns=8
+    --maximum-line-length=60
+    --stack-closing-paren</code></pre>
+
+<p>This caused an instability which was fixed by giving priority to the -bom flag. The stable state is then</p>
+
+<pre><code>        $resource = { id =&gt; $package-&gt;new_from_mana(
+                        $result-&gt;{data}
+        )-&gt;id };</code></pre>
+
+<p>This fixes case b1179.</p>
+
+<p>21 Jul 2021, 4ecc078.</p>
+
+</dd>
+<dt id="Fix-problems-with--kgb-in-complex-structures"><b>Fix problems with -kgb in complex structures</b></dt>
+<dd>
+
+<p>This update fixes two problems involving the -kgb option.</p>
+
+<p>The first problem is that testing with random parameters produced some examples of formatting instabilities involving applying --keyword-group-blanks to complex structures, particularly welded structures. The -kgb parameters work well on simple statements or simple lists, so a check was added to prevent them from working on lists which are more than one level deep. This fixes issues b1177 and b1178 without significantly changing the -kgb functionality.</p>
+
+<p>The second problem is that a terminal blank line could be misplaced if the last statement was a structure. This is illustrated with the following snippet:</p>
+
+<pre><code>    sub new {
+      my $class = shift;
+      my $number = shift || croak &quot;What?? No number??\n&quot;;
+      my $classToGenerate = shift || croak &quot;Need a class to generate, man!\n&quot;;
+      my $hash = shift; #No carp here, some operators do not need specific stuff
+      my $self = { _number =&gt; $number,
+                   _class =&gt; $classToGenerate,
+                   _hash =&gt; $hash };
+      bless $self, $class; # And bless it
+      return $self;
+    }
+
+    # OLD: perltidy -kgb -sil=0 gave
+    sub new {
+
+        my $class           = shift;
+        my $number          = shift || croak &quot;What?? No number??\n&quot;;
+        my $classToGenerate = shift || croak &quot;Need a class to generate, man!\n&quot;;
+        my $hash = shift;   #No carp here, some operators do not need specific stuff
+        my $self = {
+            _number =&gt; $number,
+
+            _class =&gt; $classToGenerate,
+            _hash  =&gt; $hash
+        };
+        bless $self, $class;    # And bless it
+        return $self;
+    }</code></pre>
+
+<p>The blank line which has appeared after the line &#39;_number =&gt;&#39; was intended to go after the closing brace but a line count was off. This has been fixed:</p>
+
+<pre><code>    # NEW: perltidy -kgb -sil=0 gives
+    sub new {
+
+        my $class           = shift;
+        my $number          = shift || croak &quot;What?? No number??\n&quot;;
+        my $classToGenerate = shift || croak &quot;Need a class to generate, man!\n&quot;;
+        my $hash = shift;   #No carp here, some operators do not need specific stuff
+        my $self = {
+            _number =&gt; $number,
+            _class  =&gt; $classToGenerate,
+            _hash   =&gt; $hash
+        };
+
+        bless $self, $class;    # And bless it
+        return $self;
+    }</code></pre>
+
+<p>This fixes issue c048.</p>
+
+<p>19 Jul 2021, 071a3f6.</p>
+
+</dd>
+<dt id="Fix-to-keep-from-losing-blank-lines-after-a-code-skipping-section"><b>Fix to keep from losing blank lines after a code-skipping section</b></dt>
+<dd>
+
+<p>A problem in the current version of perltidy is that a blank line after the closing code-skipping comment can be lost if there was a blank line before the start of the code skipping section. For example, given the following code:</p>
+
+<pre><code>    $x = 1;
+
+    #&lt;&lt;V
+    % # = ( foo =&gt; &#39;bar&#39;, baz =&gt; &#39;buz&#39; );
+    print keys(%#), &quot;\n&quot;;
+    #&gt;&gt;V
+
+    @# = ( foo, &#39;bar&#39;, baz, &#39;buz&#39; );
+    print @#, &quot;\n&quot;;</code></pre>
+
+<p>running perltidy gives:</p>
+
+<pre><code>    $x = 1;
+
+    #&lt;&lt;V
+    % # = ( foo =&gt; &#39;bar&#39;, baz =&gt; &#39;buz&#39; );
+    print keys(%#), &quot;\n&quot;;
+    #&gt;&gt;V
+    @# = ( foo, &#39;bar&#39;, baz, &#39;buz&#39; );
+    print @#, &quot;\n&quot;;</code></pre>
+
+<p>Notice that the blank line after the closing comment #&gt;&gt;V is missing. What happened is that the formatter is counting blank lines and did not &#39;see&#39; the code skipping section. So the blank after the closing comment looked like the second blank in a row, so it got removed since the default is --maximum-consecutive-blank-lines=1.</p>
+
+<p>This update fixes this by resetting the counter. This fixes case c047. A simple workaround until the next release is to include the parameter</p>
+
+<p>--maximum-consecutive-blank-lines=2, or -mbl=2.</p>
+
+<p>It can be removed after the next release.</p>
+
+<p>18 Jul 2021, 9648e16.</p>
+
+</dd>
+<dt id="Fix-possible-welding-instability-in-ternary-after-fat-comma"><b>Fix possible welding instability in ternary after fat comma</b></dt>
+<dd>
+
+<p>Random testing produced a case of formatting instability involving welding within a ternary expression following a fat comma:</p>
+
+<pre><code>    if ( $op and $op eq &#39;do_search&#39; ) {
+        @{$invoices} =
+          GetInvoices(
+              shipmentdatefrom =&gt;
+              $shipmentdatefrom ? output_pref( {
+                             str =&gt; $shipmentdatefrom,
+                             dateformat =&gt; &#39;iso&#39;
+              } )
+              : undef,
+              publicationyear =&gt; $publicationyear,
+          );
+    }</code></pre>
+
+<p>when the following specific parameters were used</p>
+
+<pre><code>    --extended-continuation-indentation
+    --line-up-parentheses
+    --maximum-line-length=38
+    --variable-maximum-line-length
+    --weld-nested-containers</code></pre>
+
+<p>This update fixes this issue, case b1174.</p>
+
+<p>18 Jul 2021, 12ae46b.</p>
+
+</dd>
+<dt id="Fix-mis-tokenization-before-pointer"><b>Fix mis-tokenization before pointer</b></dt>
+<dd>
+
+<p>Random testing produced the following case in which formatting was unstable because the variable &#39;$t&#39; was being mis-tokenized as a possible indirect object.</p>
+
+<pre><code>    --break-before-all-operators
+    --ignore-old-breakpoints
+    --maximum-line-length=22
+    -sil=0
+
+    my $json_response
+      = decode_json $t
+      -&gt;tx-&gt;res-&gt;content
+      -&gt;get_body_chunk;</code></pre>
+
+<p>This update fixes cases b1175, b1176.</p>
+
+<p>17 Jul 2021, 4aa1318.</p>
+
+</dd>
 <dt id="Fix-to-make--wn-and--bbxx-n-flags-work-together"><b>Fix to make -wn and -bbxx=n flags work together</b></dt>
 <dd>
 
 
 <p>This update fixes case b1173. It works for any number of welded containers, and the -bbxxi=n flags also work correctly.</p>
 
-<p>16 Jul 2021.</p>
+<p>16 Jul 2021, c71f882.</p>
 
 </dd>
 <dt id="Fix-problem-with-side-comment-after-pattern"><b>Fix problem with side comment after pattern</b></dt>
index ca7f6ceefd8b011a705c2df42dae7c6a6cb3ff6e..41581cc03e9bcd563bc53b071fd30824ea9d557d 100644 (file)
@@ -1,5 +1,189 @@
 <h1>Perltidy Change Log</h1>
 
+<h2>2022 02 17</h2>
+
+<pre><code>- A new flag, --encode-output-strings, or -eos, has been added to resolve
+  issue git #83. This issue involves the interface between Perl::Tidy and
+  calling programs, and Code::TidyAll (tidyall) in particular.  The problem
+  is that perltidy by default returns decoded character strings, but
+  tidyall expects encoded strings.  This flag provides a fix for that.
+
+  So, tidyall users who process encoded (utf8) files should update to this
+  version of Perl::Tidy and use -eos for tidyall.  For further info see:
+
+  https://github.com/houseabsolute/perl-code-tidyall/issues/84, and
+  https://github.com/perltidy/perltidy/issues/83
+
+  If there are other applications having utf8 problems at the interface
+  with Perl::Tidy, this flag probably may need to be set.
+
+- The default value of the new flag, --encode-output-strings, -eos, is currently
+  -neos BUT THIS MAY CHANGE in a future release because the current
+  default is inconvenient.  So authors of programs which receive character
+  strings back from Perl::Tidy should set this flag, if necessary,
+  to avoid any problems when the default changes.  For more information see the
+  above links and the Perl::Tidy man pages for example coding.
+
+- The possible values of the string 's' for the flag '--character-encoding=s'
+  have been limited to 'utf8' (or UTF-8), 'none', or 'guess'.  Previously an
+  arbitrary encoding could also be specified, but as a result of discussions
+  regarding git #83 it became clear that this could cause trouble
+  since the output encoding was still restricted to UTF-8. Users
+  who need to work in other encodings can write a short program calling
+  Perl::Tidy with pre- and post-processing to handle encoding/decoding.
+
+- A new flag --break-after-labels=i, or -bal=i, was added for git #86.  This
+  controls line breaks after labels, to provide a uniform style, as follows:
+
+        -bal=0 follows the input line breaks [DEFAULT]
+        -bal=1 always break after a label
+        -bal=2 never break after a label
+
+  For example:
+
+      # perltidy -bal=1
+      INIT:
+        {
+            $xx = 1.234;
+        }
+
+      # perltidy -bal=2
+      INIT: {
+            $xx = 1.234;
+        }
+
+- Fix issue git #82, an error handling something like ${bareword} in a
+  possible indirect object location. Perl allows this, now perltidy does too.
+
+- The flags -kbb=s or --keep-old-breakpoints-before=s, and its counterpart
+  -kba=s or --keep-old-breakpoints-after=s have expanded functionality
+  for the container tokens: { [ ( } ] ).  The updated man pages have
+  details.
+
+- Two new flags have been added to provide finer vertical alignment control,
+  --valign-exclusion-list=s (-vxl=s) and  --valign-inclusion-list=s (-vil=s).
+  This has been requested several times, most recently in git #79, and it
+  finally got done.  For example, -vil='=&gt;' means just align on '=&gt;'.
+
+- A new flag -gal=s, --grep-alias-list=s, has been added as suggested in
+  git #77.  This allows code blocks passed to list operator functions to
+  be formatted in the same way as a code block passed to grep, map, or sort.
+  By default, the following list operators in List::Util are included:
+
+    all any first none notall reduce reductions
+
+  They can be changed with the flag -gaxl=s, -grep-alias-exclusion-list=s
+
+- A new flag -xlp has been added which can be set to avoid most of the
+  limitations of the -lp flag regarding side comments, blank lines, and
+  code blocks.  See the man pages for more info. This fixes git #64 and git #74.
+  The older -lp flag still works.
+
+- A new flag -lpil=s, --line-up-parentheses-inclusion-list=s, has been added
+  as an alternative to -lpxl=s, --line-up-parentheses-exclusion-list=s.
+  It supplies equivalent information but is much easier to describe and use.
+  It works for both the older -lp version and the newer -xlp.
+
+- The coding for the older -lp flag has been updated to avoid some problems
+  and limitations.  The new coding allows the -lp indentation style to
+  mix smoothly with the standard indentation in a single file.  Some problems
+  where -lp and -xci flags were not working well together have been fixed, such
+  as happened in issue rt140025.  As a result of these updates some minor
+  changes in existing code using the -lp style may occur.
+
+- This version of perltidy was stress-tested for many cpu hours with
+  random input parameters. No failures to converge, internal fault checks,
+  undefined variable references or other irregularities were seen.
+
+- Numerous minor fixes have been made, mostly very rare formatting
+  instabilities found in random testing.
+</code></pre>
+
+<h2>2021 10 29</h2>
+
+<pre><code>- No significant bugs have been found since the last release, but several
+  minor issues have been fixed.  Vertical alignment has been improved for
+  lists of call args which are not contained within parens (next item).
+
+- Vertical alignment of function calls without parens has been improved with
+  the goal of making vertical alignment essentially the same with or
+  without parens around the call args.  Some examples:
+
+    # OLD
+    mkTextConfig $c, $x, $y, -anchor =&gt; 'se', $color;
+    mkTextConfig $c, $x + 30, $y, -anchor =&gt; 's',  $color;
+    mkTextConfig $c, $x + 60, $y, -anchor =&gt; 'sw', $color;
+    mkTextConfig $c, $x, $y + 30, -anchor =&gt; 'e', $color;
+
+    # NEW
+    mkTextConfig $c, $x,      $y,      -anchor =&gt; 'se', $color;
+    mkTextConfig $c, $x + 30, $y,      -anchor =&gt; 's',  $color;
+    mkTextConfig $c, $x + 60, $y,      -anchor =&gt; 'sw', $color;
+    mkTextConfig $c, $x,      $y + 30, -anchor =&gt; 'e',  $color;
+
+    # OLD
+    is id_2obj($id), undef, "unregistered object not retrieved";
+    is scalar keys %$ob_reg, 0, "object registry empty";
+    is register($obj), $obj, "object returned by register";
+    is scalar keys %$ob_reg, 1, "object registry nonempty";
+    is id_2obj($id), $obj, "registered object retrieved";
+
+    # NEW
+    is id_2obj($id),         undef, "unregistered object not retrieved";
+    is scalar keys %$ob_reg, 0,     "object registry empty";
+    is register($obj),       $obj,  "object returned by register";
+    is scalar keys %$ob_reg, 1,     "object registry nonempty";
+    is id_2obj($id),         $obj,  "registered object retrieved";
+
+  This will cause some changes in alignment, hopefully for the better,
+  particularly in test code which often uses numerous parenless function
+  calls with functions like 'ok', 'is', 'is_deeply', ....
+
+- Two new parameters were added to control the block types to which the
+  -bl (--opening-brace-on-new-line) flag applies.  The new parameters are
+  -block-left-list=s, or -bll=s, and --block-left-exclusion-list=s,
+  or -blxl=s.  Previously the -bl flag was 'hardwired' to apply to
+  nearly all blocks. The default values of the new parameters
+  retain the the old default behavior but allow it to be changed.
+
+- The default behavior of the -bli (-brace-left-and-indent) flag has changed
+  slightly.  Previously, if you set -bli, then the -bl flag would also
+  automatically be set.  Consequently, block types which were not included
+  in the default list for -bli would get -bl formatting.  This is no longer done,
+  and these two styles are now controlled independently.  The manual describes
+  the controls.  If you want to recover the exact previous default behavior of
+  the -bli then add the -bl flag.
+
+- A partial fix was made for issue for git #74. The -lp formatting style was
+  being lost when a one-line anonymous sub was followed by a closing brace.
+
+- Fixed issue git #73, in which the -nfpva flag was not working correctly.
+  Some unwanted vertical alignments of spaced function perens
+  were being made.
+
+- Updated the man pages to clarify the flags -valign and -novalign
+  for turning vertical alignment on and off (issue git #72).
+  Added parameters -vc -vsc -vbc for separately turning off vertical
+  alignment of code, side comments and block comments.
+
+- Fixed issue git #68, where a blank line following a closing code-skipping
+  comment, '#&gt;&gt;V', could be lost.
+
+- This version runs 10 to 15 percent faster on large files than the
+  previous release due to optimizations made with the help of NYTProf.
+
+- This version of perltidy was stress-tested for many cpu hours with
+  random input parameters. No instabilities,  internal fault checks,
+  undefined variable references or other irregularities were seen.
+
+- Numerous minor fixes have been made, mostly very rare formatting instabilities
+  found in random testing. An effort has been made to minimize changes to
+  existing formatting that these fixes produce, but occasional changes
+  may occur. Many of these updates are listed at:
+
+       https://github.com/perltidy/perltidy/blob/master/local-docs/BugLog.pod
+</code></pre>
+
 <h2>2021 07 17</h2>
 
 <pre><code>- This release is being made mainly because of the next item, in which an
   comment '#&lt;&lt;V' which is not terminated with a closing comment '#&gt;&gt;V'. This
   makes code-skipping and format-skipping behave in a similar way: an
   opening comment without a corresponding closing comment will cause
-  the rest of a file to be skipped.  If there is a question about which lines 
-  are skipped, a .LOG file can be produced with the -g flag and it will have 
+  the rest of a file to be skipped.  If there is a question about which lines
+  are skipped, a .LOG file can be produced with the -g flag and it will have
   this information.
 
 - Removed the limit on -ci=n when -xci is set, reference: rt #136415.
@@ -86,14 +270,14 @@ control over which containers get -lp indentation.
 flags and the --line-up-parens flag.
 
 - Fixed issue git #54 regarding irregular application of the --break-before-paren
-and similar --break-before-xxx flags, in which lists without commas were not 
+and similar --break-before-xxx flags, in which lists without commas were not
 being formatted according to these flags.
 
-- Fixed issue git #53. A flag was added to turn off alignment of spaced function 
+- Fixed issue git #53. A flag was added to turn off alignment of spaced function
 parens.  If the --space-function-paren, -sfp flag is set, a side-effect is that the
 spaced function parens may get vertically aligned.  This can be undesirable,
 so a new parameter '--function-paren-vertical-alignment', or '-fpva', has been
-added to turn this vertical alignment off. The default is '-fpva', so that 
+added to turn this vertical alignment off. The default is '-fpva', so that
 existing formatting is not changed.  Use '-nfpva' to turn off unwanted
 vertical alignment.  To illustrate the possibilities:
 
@@ -202,9 +386,9 @@ signatures.  Reformatting with the current version will fix the problem.
 
 <h2>2020 12 01</h2>
 
-<pre><code>- This release is being made primarily to make available a several new formatting 
-  parameters, in particular -xci, -kbb=s, -kba=s, and -wnxl=s. No significant 
-  bugs have been found since the previous release, but numerous minor issues have 
+<pre><code>- This release is being made primarily to make available a several new formatting
+  parameters, in particular -xci, -kbb=s, -kba=s, and -wnxl=s. No significant
+  bugs have been found since the previous release, but numerous minor issues have
   been found and fixed as listed below.
 
 - This version is about 20% faster than the previous version due to optimizations
@@ -217,7 +401,7 @@ signatures.  Reformatting with the current version will fix the problem.
 
 - Fixed issue git #45, -vtc=n flag was ignored when -wn was set.
 
-- implement request RT #133649, delete-old-newlines selectively. Two parameters, 
+- implement request RT #133649, delete-old-newlines selectively. Two parameters,
 
   -kbb=s or --keep-old-breakpoints-before=s, and
   -kba=s or --keep-old-breakpoints-after=s
index 7f1f7fc162e9054e89d42cd9e257451fb5b9eb69..3b47e6c43e4684df3f9544e1c48d5c19cbac2cc9 100644 (file)
@@ -69,9 +69,9 @@
         destination       - the destination of the formatted output
         stderr            - standard error output
         perltidyrc        - the .perltidyrc file
-        logfile           - the .LOG file stream, if any 
+        logfile           - the .LOG file stream, if any
         errorfile         - the .ERR file stream, if any
-        dump_options      - ref to a hash to receive parameters (see below), 
+        dump_options      - ref to a hash to receive parameters (see below),
         dump_options_type - controls contents of dump_options
         dump_getopt_flags - ref to a hash to receive Getopt flags
         dump_options_category - ref to a hash giving category of options
 
 <dl>
 
-<dt id="source">source</dt>
+<dt id="source"><b>source</b></dt>
 <dd>
 
 <p>If the <b>source</b> parameter is given, it defines the source of the input stream. If an input stream is defined with the <b>source</b> parameter then no other source filenames may be specified in the @ARGV array or <b>argv</b> parameter.</p>
 
 </dd>
-<dt id="destination">destination</dt>
+<dt id="destination"><b>destination</b></dt>
 <dd>
 
 <p>If the <b>destination</b> parameter is given, it will be used to define the file or memory location to receive output of perltidy.</p>
 
+<p><b>Important note if destination is a string or array reference</b>. Perl strings of characters which are decoded as utf8 by Perl::Tidy can be returned in either of two possible states, decoded or encoded, and it is important that the calling program and Perl::Tidy are in agreement regarding the state to be returned. A flag <b>--encode-output-strings</b>, or simply <b>-eos</b>, was added in versions of Perl::Tidy after 20220101 for this purpose. This flag should be added to the end of the <b>argv</b> paremeter (described below) if Perl::Tidy will be decoding utf8 text. The options are as follows.</p>
+
+<ul>
+
+<li><p>Use <b>-eos</b> if Perl::Tidy should encode any string which it decodes. This is probably most convenient for most programs. But do not use this setting if the calling program will encode the data too, because double encoding will corrupt data.</p>
+
+</li>
+<li><p>Use <b>-neos</b> if a string should remain decoded if it was decoded by Perl::Tidy. This is appropriate if the calling program will handle any needed encoding before outputting the string.</p>
+
+</li>
+<li><p>The current default is <b>-neos</b>, but <b>the default could change in a future version</b>, so <b>-neos</b> should still be set, if appropriate, to allow for the possibility of a future change in the default.</p>
+
+</li>
+</ul>
+
+<p>For example, to set <b>-eos</b> the following could be used</p>
+
+<pre><code>        $argv .= &quot; -eos&quot; if ( $Perl::Tidy::VERSION &gt; 20220101 );
+
+        $error_flag = Perl::Tidy::perltidy(
+            argv        =&gt; $argv,
+            source      =&gt; \$source,
+            destination =&gt; \$destination,
+            stderr      =&gt; \$stderr,
+            errorfile   =&gt; \$errorfile
+        );</code></pre>
+
+<p>The test on version allows older versions of Perl::Tidy to still be used.</p>
+
+<p>For some background information see <a href="https://github.com/perltidy/perltidy/issues/83">https://github.com/perltidy/perltidy/issues/83</a> and <a href="https://github.com/houseabsolute/perl-code-tidyall/issues/84">https://github.com/houseabsolute/perl-code-tidyall/issues/84</a>.</p>
+
 </dd>
-<dt id="stderr">stderr</dt>
+<dt id="stderr"><b>stderr</b></dt>
 <dd>
 
 <p>The <b>stderr</b> parameter allows the calling program to redirect the stream that would otherwise go to the standard error output device to any of the stream types listed above. This stream contains important warnings and errors related to the parameters passed to perltidy.</p>
 
 </dd>
-<dt id="perltidyrc">perltidyrc</dt>
+<dt id="perltidyrc"><b>perltidyrc</b></dt>
 <dd>
 
 <p>If the <b>perltidyrc</b> file is given, it will be used instead of any <i>.perltidyrc</i> configuration file that would otherwise be used.</p>
 
 </dd>
-<dt id="errorfile">errorfile</dt>
+<dt id="errorfile"><b>errorfile</b></dt>
 <dd>
 
 <p>The <b>errorfile</b> parameter allows the calling program to capture the stream that would otherwise go to either a .ERR file. This stream contains warnings or errors related to the contents of one source file or stream.</p>
 <p>However if perltidy is called to process just a single perl script then it may be more convenient to combine the <b>errorfile</b> stream with the <b>stderr</b> stream. This can be done by setting the <b>-se</b> parameter, in which case this parameter is ignored.</p>
 
 </dd>
-<dt id="logfile">logfile</dt>
+<dt id="logfile"><b>logfile</b></dt>
 <dd>
 
 <p>The <b>logfile</b> parameter allows the calling program to capture the log stream. This stream is only created if requested with a <b>-g</b> parameter. It contains detailed diagnostic information about a script which may be useful for debugging.</p>
 
 </dd>
-<dt id="teefile">teefile</dt>
+<dt id="teefile"><b>teefile</b></dt>
 <dd>
 
 <p>The <b>teefile</b> parameter allows the calling program to capture the tee stream. This stream is only created if requested with one of the &#39;tee&#39; parameters, a <b>--tee-pod</b> , <b>--tee-block-comments</b>, <b>--tee-side-commnts</b>, or <b>--tee-all-comments</b>.</p>
 
 </dd>
-<dt id="debugfile">debugfile</dt>
+<dt id="debugfile"><b>debugfile</b></dt>
 <dd>
 
 <p>The <b>debugfile</b> parameter allows the calling program to capture the stream produced by the <b>--DEBUG</b> parameter. This parameter is mainly used for debugging perltidy itself.</p>
 
 </dd>
-<dt id="argv">argv</dt>
+<dt id="argv"><b>argv</b></dt>
 <dd>
 
 <p>If the <b>argv</b> parameter is given, it will be used instead of the <b>@ARGV</b> array. The <b>argv</b> parameter may be a string, a reference to a string, or a reference to an array. If it is a string or reference to a string, it will be parsed into an array of items just as if it were a command line string.</p>
 
 </dd>
-<dt id="dump_options">dump_options</dt>
+<dt id="dump_options"><b>dump_options</b></dt>
 <dd>
 
 <p>If the <b>dump_options</b> parameter is given, it must be the reference to a hash. In this case, the parameters contained in any perltidyrc configuration file will be placed in this hash and perltidy will return immediately. This is equivalent to running perltidy with --dump-options, except that the parameters are returned in a hash rather than dumped to standard output. Also, by default only the parameters in the perltidyrc file are returned, but this can be changed (see the next parameter). This parameter provides a convenient method for external programs to read a perltidyrc file. An example program using this feature, <i>perltidyrc_dump.pl</i>, is included in the distribution.</p>
 <p>Any combination of the <b>dump_</b> parameters may be used together.</p>
 
 </dd>
-<dt id="dump_options_type">dump_options_type</dt>
+<dt id="dump_options_type"><b>dump_options_type</b></dt>
 <dd>
 
 <p>This parameter is a string which can be used to control the parameters placed in the hash reference supplied by <b>dump_options</b>. The possible values are &#39;perltidyrc&#39; (default) and &#39;full&#39;. The &#39;full&#39; parameter causes both the default options plus any options found in a perltidyrc file to be returned.</p>
 
 </dd>
-<dt id="dump_getopt_flags">dump_getopt_flags</dt>
+<dt id="dump_getopt_flags"><b>dump_getopt_flags</b></dt>
 <dd>
 
 <p>If the <b>dump_getopt_flags</b> parameter is given, it must be the reference to a hash. This hash will receive all of the parameters that perltidy understands and flags that are passed to Getopt::Long. This parameter may be used alone or with the <b>dump_options</b> flag. Perltidy will exit immediately after filling this hash. See the demo program <i>perltidyrc_dump.pl</i> for example usage.</p>
 
 </dd>
-<dt id="dump_options_category">dump_options_category</dt>
+<dt id="dump_options_category"><b>dump_options_category</b></dt>
 <dd>
 
 <p>If the <b>dump_options_category</b> parameter is given, it must be the reference to a hash. This hash will receive a hash with keys equal to all long parameter names and values equal to the title of the corresponding section of the perltidy manual. See the demo program <i>perltidyrc_dump.pl</i> for example usage.</p>
 
 </dd>
-<dt id="dump_abbreviations">dump_abbreviations</dt>
+<dt id="dump_abbreviations"><b>dump_abbreviations</b></dt>
 <dd>
 
 <p>If the <b>dump_abbreviations</b> parameter is given, it must be the reference to a hash. This hash will receive all abbreviations used by Perl::Tidy. See the demo program <i>perltidyrc_dump.pl</i> for example usage.</p>
 
 </dd>
-<dt id="prefilter">prefilter</dt>
+<dt id="prefilter"><b>prefilter</b></dt>
 <dd>
 
 <p>A code reference that will be applied to the source before tidying. It is expected to take the full content as a string in its input, and output the transformed content.</p>
 
 </dd>
-<dt id="postfilter">postfilter</dt>
+<dt id="postfilter"><b>postfilter</b></dt>
 <dd>
 
 <p>A code reference that will be applied to the tidied result before outputting. It is expected to take the full content as a string in its input, and output the transformed content.</p>
 <p>The following example uses string references to hold the input and output code and error streams, and illustrates checking for errors.</p>
 
 <pre><code>  use Perl::Tidy;
-  
+
   my $source_string = &lt;&lt;&#39;EOT&#39;;
   my$error=Perl::Tidy::perltidy(argv=&gt;$argv,source=&gt;\$source_string,
     destination=&gt;\$dest_string,stderr=&gt;\$stderr_string,
   errorfile=&gt;\$errorfile_string,);
   EOT
-  
+
   my $dest_string;
   my $stderr_string;
   my $errorfile_string;
   $argv .= &quot; -nst&quot;;     # Must turn off -st in case -pbp is specified
   $argv .= &quot; -se&quot;;      # -se appends the errorfile to stderr
   ## $argv .= &quot; --spell-check&quot;;  # uncomment to trigger an error
-  
+
   print &quot;&lt;&lt;RAW SOURCE&gt;&gt;\n$source_string\n&quot;;
-  
+
   my $error = Perl::Tidy::perltidy(
       argv        =&gt; $argv,
       source      =&gt; \$source_string,
       errorfile   =&gt; \$errorfile_string,    # ignored when -se flag is set
       ##phasers   =&gt; &#39;stun&#39;,                # uncomment to trigger an error
   );
-  
+
   if ($error) {
-  
+
       # serious error in input parameters, no tidied output
       print &quot;&lt;&lt;STDERR&gt;&gt;\n$stderr_string\n&quot;;
       die &quot;Exiting because of serious errors\n&quot;;
   }
-  
+
   if ($dest_string)      { print &quot;&lt;&lt;TIDIED SOURCE&gt;&gt;\n$dest_string\n&quot; }
   if ($stderr_string)    { print &quot;&lt;&lt;STDERR&gt;&gt;\n$stderr_string\n&quot; }
   if ($errorfile_string) { print &quot;&lt;&lt;.ERR file&gt;&gt;\n$errorfile_string\n&quot; }</code></pre>
 <p>The <b>formatter</b> parameter is an optional callback object which allows the calling program to receive tokenized lines directly from perltidy for further specialized processing. When this parameter is used, the two formatting options which are built into perltidy (beautification or html) are ignored. The following diagram illustrates the logical flow:</p>
 
 <pre><code>                    |-- (normal route)   -&gt; code beautification
-  caller-&gt;perltidy-&gt;|-- (-html flag )    -&gt; create html 
+  caller-&gt;perltidy-&gt;|-- (-html flag )    -&gt; create html
                     |-- (formatter given)-&gt; callback to write_line</code></pre>
 
 <p>This can be useful for processing perl scripts in some way. The parameter <code>$formatter</code> in the perltidy call,</p>
 
-<pre><code>        formatter   =&gt; $formatter,  </code></pre>
+<pre><code>        formatter   =&gt; $formatter,</code></pre>
 
 <p>is an object created by the caller with a <code>write_line</code> method which will accept and process tokenized lines, one line per call. Here is a simple example of a <code>write_line</code> which merely prints the line number, the line type (as determined by perltidy), and the text of the line:</p>
 
 <pre><code> sub write_line {
+
      # This is called from perltidy line-by-line
      my $self              = shift;
      my $line_of_tokens    = shift;
 <p>Most applications will be only interested in lines of type <b>CODE</b>. For another example, let&#39;s write a program which checks for one of the so-called <i>naughty matching variables</i> <code>&amp;`</code>, <code>$&amp;</code>, and <code>$&#39;</code>, which can slow down processing. Here is a <b>write_line</b>, from the example program <b>find_naughty.pl</b>, which does that:</p>
 
 <pre><code> sub write_line {
+
      # This is called back from perltidy line-by-line
      # We&#39;re looking for $`, $&amp;, and $&#39;
      my ( $self, $line_of_tokens ) = @_;
+
      # pull out some stuff we might need
      my $line_type         = $line_of_tokens-&gt;{_line_type};
      my $input_line_number = $line_of_tokens-&gt;{_line_number};
      my $rtoken_type       = $line_of_tokens-&gt;{_rtoken_type};
      my $rtokens           = $line_of_tokens-&gt;{_rtokens};
      chomp $input_line;
+
      # skip comments, pod, etc
      return if ( $line_type ne &#39;CODE&#39; );
+
      # loop over tokens looking for $`, $&amp;, and $&#39;
      for ( my $j = 0 ; $j &lt; @$rtoken_type ; $j++ ) {
+
          # we only want to examine token types &#39;i&#39; (identifier)
          next unless $$rtoken_type[$j] eq &#39;i&#39;;
+
          # pull out the actual token text
          my $token = $$rtokens[$j];
+
          # and check it
          if ( $token =~ /^\$[\`\&amp;\&#39;]$/ ) {
              print STDERR
 
 <h1 id="VERSION">VERSION</h1>
 
-<p>This man page documents Perl::Tidy version 20210717</p>
+<p>This man page documents Perl::Tidy version 20220217</p>
 
 <h1 id="LICENSE">LICENSE</h1>
 
index ec997a403b870b572e6ccc8322ebcf0db3628db4..dd4382895bd8fbe7d45e881b0333c034efafa8a7 100644 (file)
 
 <p>For example,</p>
 
-<pre><code>        perltidy -sal=&#39;method fun _sub M4&#39; </code></pre>
+<pre><code>        perltidy -sal=&#39;method fun _sub M4&#39;</code></pre>
 
 <p>will cause the perltidy to treate the words &#39;method&#39;, &#39;fun&#39;, &#39;_sub&#39; and &#39;M4&#39; to be treated the same as if they were &#39;sub&#39;. Note that if the alias words are separated by spaces then the string of words should be placed in quotes.</p>
 
 <p>Note that several other parameters accept a list of keywords, including &#39;sub&#39; (see <a href="#Specifying-Block-Types">&quot;Specifying Block Types&quot;</a>). You do not need to include any sub aliases in these lists. Just include keyword &#39;sub&#39; if you wish, and all aliases are automatically included.</p>
 
+</dd>
+<dt id="gal-s---grep-alias-list-s"><b>-gal=s</b>, <b>--grep-alias-list=s</b></dt>
+<dd>
+
+<p>This flag allows a code block following an external &#39;list operator&#39; function to be formatted as if it followed one of the builtin keywords <b>grep</b>, <b>map</b> or <b>sort</b>. The string <b>s</b> contains the names of one or more such list operators, separated by spaces or commas.</p>
+
+<p>By &#39;list operator&#39; is meant a function which is invoked in the form</p>
+
+<pre><code>      word {BLOCK} @list</code></pre>
+
+<p>Perltidy tries to keep code blocks for these functions intact, since they are usually short, and does not automatically break after the closing brace since a list may follow. It also does some special handling of continuation indentation.</p>
+
+<p>For example, the code block arguments to functions &#39;My_grep&#39; and &#39;My_map&#39; can be given formatting like &#39;grep&#39; with</p>
+
+<pre><code>        perltidy -gal=&#39;My_grep My_map&#39;</code></pre>
+
+<p>By default, the following list operators in List::Util are automatically included:</p>
+
+<pre><code>      all any first none notall reduce reductions</code></pre>
+
+<p>Any operators specified with <b>--grep-alias-list</b> are added to this list. The next parameter can be used to remove words from this default list.</p>
+
+</dd>
+<dt id="gaxl-s---grep-alias-exclusion-list-s"><b>-gaxl=s</b>, <b>--grep-alias-exclusion-list=s</b></dt>
+<dd>
+
+<p>The <b>-gaxl=s</b> flag provides a method for removing any of the default list operators given above by listing them in the string <b>s</b>. To remove all of the default operators use <b>-gaxl=&#39;*&#39;</b>.</p>
+
 </dd>
 </dl>
 
 <dt id="vmll---variable-maximum-line-length"><b>-vmll</b>, <b>--variable-maximum-line-length</b></dt>
 <dd>
 
-<p>A problem arises using a fixed maximum line length with very deeply nested code and data structures because eventually the amount of leading whitespace used for indicating indentation takes up most or all of the available line width, leaving little or no space for the actual code or data. One solution is to use a vary long line length. Another solution is to use the <b>-vmll</b> flag, which basically tells perltidy to ignore leading whitespace when measuring the line length.</p>
+<p>A problem arises using a fixed maximum line length with very deeply nested code and data structures because eventually the amount of leading whitespace used for indicating indentation takes up most or all of the available line width, leaving little or no space for the actual code or data. One solution is to use a very long line length. Another solution is to use the <b>-vmll</b> flag, which basically tells perltidy to ignore leading whitespace when measuring the line length.</p>
 
 <p>To be precise, when the <b>-vmll</b> parameter is set, the maximum line length of a line of code will be M+L*I, where</p>
 
 <dt id="enc-s---character-encoding-s"><b>-enc=s</b>, <b>--character-encoding=s</b></dt>
 <dd>
 
-<p>This flag indicates the character encoding, if any, of the input data stream. Perltidy does not look for the encoding directives in the soure stream, such as <b>use utf8</b>, and instead relies on this flag to determine the encoding. (Note that perltidy often works on snippets of code rather than complete files so it cannot rely on <b>use utf8</b> directives).</p>
+<p>This flag indicates if the input data stream use a character encoding. Perltidy does not look for the encoding directives in the soure stream, such as <b>use utf8</b>, and instead relies on this flag to determine the encoding. (Note that perltidy often works on snippets of code rather than complete files so it cannot rely on <b>use utf8</b> directives).</p>
 
-<p>The possible values for <b>s</b> are (1) the name of an encoding recognized by the Encode.pm module, (2) <b>none</b> if no encoding is used, or (3) &lt;guess&gt; if perltidy should guess.</p>
+<p>The possible values for <b>s</b> are:</p>
 
-<p>For example, the value <b>utf8</b> causes the stream to be read and written as UTF-8. If the input stream cannot be decoded with a specified encoding then processing is not done.</p>
+<pre><code> -enc=none if no encoding is used, or
+ -enc=utf8 for encoding in utf8
+ -enc=guess if perltidy should guess between these two possibilities.</code></pre>
 
 <p>The value <b>none</b> causes the stream to be processed without special encoding assumptions. This is appropriate for files which are written in single-byte character encodings such as latin-1.</p>
 
-<p>The value <b>guess</b> tells perltidy to guess between either utf8 encoding or no encoding (meaning one character per byte). The guess uses the Encode::Guess module and this restricted range of guesses covers the most common cases. Testing showed that considering any greater number of encodings as guess suspects is too risky.</p>
+<p>The value <b>utf8</b> causes the stream to be read and written as UTF-8. If the input stream cannot be decoded with this encoding then processing is not done.</p>
+
+<p>The value <b>guess</b> tells perltidy to guess between either utf8 encoding or no encoding (meaning one character per byte). The <b>guess</b> option uses the Encode::Guess module which has been found to be reliable at detecting if a file is encoded in utf8 or not.</p>
 
 <p>The current default is <b>guess</b>.</p>
 
-<p>The abbreviations <b>-utf8</b> or <b>-UTF8</b> are equivalent to <b>-enc=utf8</b>, and the abbreviation <b>-guess</b> is equivalent to &lt;-enc=guess&gt;. So to process a file named <b>file.pl</b> which is encoded in UTF-8 you can use:</p>
+<p>The abbreviations <b>-utf8</b> or <b>-UTF8</b> are equivalent to <b>-enc=utf8</b>, and the abbreviation <b>-guess</b> is equivalent to <b>-enc=guess</b>. So to process a file named <b>file.pl</b> which is encoded in UTF-8 you can use:</p>
 
 <pre><code>   perltidy -utf8 file.pl</code></pre>
 
-<p>or perltidy -guess file.pl</p>
+<p>or</p>
+
+<pre><code>   perltidy -guess file.pl</code></pre>
+
+<p>or simply</p>
+
+<pre><code>   perltidy file.pl</code></pre>
+
+<p>since <b>-guess</b> is the default.</p>
+
+<p>To process files with an encoding other than UTF-8, it would be necessary to write a short program which calls the Perl::Tidy module with some pre- and post-processing to handle decoding and encoding.</p>
 
-<p>To process a file in <b>euc-jp</b> you could use</p>
+</dd>
+<dt id="eos-s---encode-output-strings-s"><b>-eos=s</b>, <b>--encode-output-strings=s</b></dt>
+<dd>
+
+<p>This flag has been added to resolve an issue involving the interface between Perl::Tidy and calling programs, and in particular <b>Code::TidyAll (tidyall)</b>. By default Perl::Tidy returns unencoded strings to the calling program, but some programs expect encoded strings. Setting this flag causes Perl::Tidy to return encoded output strings which it decoded. For some background information see <a href="https://github.com/perltidy/perltidy/issues/83">https://github.com/perltidy/perltidy/issues/83</a> and <a href="https://github.com/houseabsolute/perl-code-tidyall/issues/84">https://github.com/houseabsolute/perl-code-tidyall/issues/84</a>.</p>
 
-<pre><code>   perltidy -enc=euc-jp file.pl</code></pre>
+<p>If you only run the <b>perltidy</b> binary this flag has no effect.</p>
 
-<p>A perltidy output file is unencoded if the input file is unencoded, and otherwise it is encoded as <b>utf8</b>, even if the input encoding was not <b>utf8</b>.</p>
+<p>If you use <b>tidyall</b> with encoded files and encounter irregularities such as <b>wide character</b> messages you should set this flag.</p>
+
+<p>Additional information can be found in the man pages for the <b>Perl::Tidy</b> module.</p>
 
 </dd>
 <dt id="gcs---use-unicode-gcstring"><b>-gcs</b>, <b>--use-unicode-gcstring</b></dt>
 
 <p>Continuation indentation is extra indentation spaces applied when a long line is broken. The default is n=2, illustrated here:</p>
 
-<pre><code> my $level =   # -ci=2      
+<pre><code> my $level =   # -ci=2
    ( $max_index_to_go &gt;= 0 ) ? $levels_to_go[0] : $last_output_level;</code></pre>
 
 <p>The same example, with n=0, is a little harder to read:</p>
 
-<pre><code> my $level =   # -ci=0    
+<pre><code> my $level =   # -ci=0
  ( $max_index_to_go &gt;= 0 ) ? $levels_to_go[0] : $last_output_level;</code></pre>
 
 <p>The value given to <b>-ci</b> is also used by some commands when a small space is required. Examples are commands for outdenting labels, <b>-ola</b>, and control keywords, <b>-okw</b>.</p>
 <p>If the default method does not work correctly, or you want to change the starting level, use <b>-sil=n</b>, to force the starting level to be n.</p>
 
 </dd>
-<dt id="List-indentation-using--lp---line-up-parentheses"><b>List indentation</b> using <b>-lp</b>, <b>--line-up-parentheses</b></dt>
+<dt id="List-indentation-using---line-up-parentheses--lp-or---extended--line-up-parentheses--xlp"><b>List indentation</b> using <b>--line-up-parentheses</b>, <b>-lp</b> or <b>--extended--line-up-parentheses</b> , <b>-xlp</b></dt>
 <dd>
 
-<p>By default, perltidy indents lists with 4 spaces, or whatever value is specified with <b>-i=n</b>. Here is a small list formatted in this way:</p>
+<p>These flags provide an alternative indentation method for list data. The original flag for this is <b>-lp</b>, but it has some limitations (explained below) which are avoided with the newer <b>-xlp</b> flag. So <b>-xlp</b> is probably the better choice for new work, but the <b>-lp</b> flag is retained to minimize changes to existing formatting. If you enter both <b>-lp</b> and <b>-xlp</b>, then <b>-xlp</b> will be used.</p>
+
+<p>In the default indentation method perltidy indents lists with 4 spaces, or whatever value is specified with <b>-i=n</b>. Here is a small list formatted in this way:</p>
 
 <pre><code>    # perltidy (default)
     @month_of_year = (
         &#39;Jul&#39;, &#39;Aug&#39;, &#39;Sep&#39;, &#39;Oct&#39;, &#39;Nov&#39;, &#39;Dec&#39;
     );</code></pre>
 
-<p>Use the <b>-lp</b> flag to add extra indentation to cause the data to begin past the opening parentheses of a sub call or list, or opening square bracket of an anonymous array, or opening curly brace of an anonymous hash. With this option, the above list would become:</p>
+<p>The <b>-lp</b> or <b>-xlp</b> flags add extra indentation to cause the data to begin past the opening parentheses of a sub call or list, or opening square bracket of an anonymous array, or opening curly brace of an anonymous hash. With this option, the above list would become:</p>
 
-<pre><code>    # perltidy -lp
+<pre><code>    # perltidy -lp or -xlp
     @month_of_year = (
                        &#39;Jan&#39;, &#39;Feb&#39;, &#39;Mar&#39;, &#39;Apr&#39;, &#39;May&#39;, &#39;Jun&#39;,
                        &#39;Jul&#39;, &#39;Aug&#39;, &#39;Sep&#39;, &#39;Oct&#39;, &#39;Nov&#39;, &#39;Dec&#39;
 
 <p>If the available line length (see <b>-l=n</b> ) does not permit this much space, perltidy will use less. For alternate placement of the closing paren, see the next section.</p>
 
-<p>This option has no effect on code BLOCKS, such as if/then/else blocks, which always use whatever is specified with <b>-i=n</b>.</p>
+<p>These flags have no effect on code BLOCKS, such as if/then/else blocks, which always use whatever is specified with <b>-i=n</b>.</p>
+
+<p>Some limitiations on these flags are:</p>
+
+<ul>
+
+<li><p>A limitation on <b>-lp</b>, but not <b>-xlp</b>, occurs in situations where perltidy does not have complete freedom to choose line breaks. Then it may temporarily revert to its default indentation method. This can occur for example if there are blank lines, block comments, multi-line quotes, or side comments between the opening and closing parens, braces, or brackets. It will also occur if a multi-line anonymous sub occurs within a container since that will impose specific line breaks (such as line breaks after statements).</p>
+
+</li>
+<li><p>For both the <b>-lp</b> and <b>-xlp</b> flags, any parameter which significantly restricts the ability of perltidy to choose newlines will conflict with these flags and will cause them to be deactivated. These include <b>-io</b>, <b>-fnl</b>, <b>-nanl</b>, and <b>-ndnl</b>.</p>
+
+</li>
+<li><p>The <b>-lp</b> and <b>-xlp</b> options may not be used together with the <b>-t</b> tabs option. They may, however, be used with the <b>-et=n</b> tab method</p>
+
+</li>
+</ul>
+
+<p>There are some potential disadvantages of this indentation method compared to the default method that should be noted:</p>
+
+<ul>
+
+<li><p>The available line length can quickly be used up if variable names are long. This can cause deeply nested code to quickly reach the line length limit, and become badly formatted, much sooner than would occur with the default indentation method.</p>
+
+</li>
+<li><p>Since the indentation depends on the lengths of variable names, small changes in variable names can cause changes in indentation over many lines in a file. This means that minor name changes can produce significant file differences. This can be annoying and does not occur with the default indentation method.</p>
+
+</li>
+</ul>
+
+<p>Some things that can be done to minimize these problems are:</p>
 
-<p>In situations where perltidy does not have complete freedom to choose line breaks it may temporarily revert to its default indentation method. This can occur for example if there are blank lines, block comments, multi-line quotes, or side comments between the opening and closing parens, braces, or brackets.</p>
+<ul>
 
-<p>In addition, any parameter which significantly restricts the ability of perltidy to choose newlines will conflict with <b>-lp</b> and will cause <b>-lp</b> to be deactivated. These include <b>-io</b>, <b>-fnl</b>, <b>-nanl</b>, and <b>-ndnl</b>. The reason is that the <b>-lp</b> indentation style can require the careful coordination of an arbitrary number of break points in hierarchical lists, and these flags may prevent that.</p>
+<li><p>Increase <b>--maximum-line-length=n</b> above the default <b>n=80</b> characters if necessary.</p>
 
-<p>The <b>-lp</b> option may not be used together with the <b>-t</b> tabs option. It may, however, be used with the <b>-et=n</b> tab method.</p>
+</li>
+<li><p>If you use <b>-xlp</b> then long side comments can limit the indentation over multiple lines. Consider adding the flag <b>--ignore-side-comment-lengths</b> to prevent this, or minimizing the use of side comments.</p>
+
+</li>
+<li><p>Apply this style in a limited way. By default, it applies to all list containers (not just lists in parentheses). The next section describes how to limit this style to, for example, just function calls. The default indentation method will be applied elsewhere.</p>
+
+</li>
+</ul>
 
 </dd>
-<dt id="lpxl-s---line-up-parentheses-exclusion-list"><b>-lpxl=s</b>, <b>--line-up-parentheses-exclusion-list</b></dt>
+<dt id="lpil-s---line-up-parentheses-inclusion-list-and--lpxl-s---line-up-parentheses-exclusion-list"><b>-lpil=s</b>, <b>--line-up-parentheses-inclusion-list</b> and <b>-lpxl=s</b>, <b>--line-up-parentheses-exclusion-list</b></dt>
 <dd>
 
-<p>This is an experimental parameter; the details might change as experience with it is gained.</p>
+<p>The following discussion is written for <b>-lp</b> but applies equally to the newer <b>-xlp</b> version. By default, the <b>-lp</b> flag applies to as many containers as possible. The set of containers to which the <b>-lp</b> style applies can be reduced by either one of these two flags:</p>
 
-<p>The <b>-lp</b> indentation style works well for some types of coding but can produce very long lines when variables have long names and/or containers are very deeply nested. The <b>-lpxl=s</b> flag is intended to help mitigate this problem by providing control over the containers to which the <b>-lp</b> indentation style is applied. The <b>-lp</b> flag by default is &quot;greedy&quot; and applies to as many containers as possible. This flag specifies a list of things which should <b>not</b> be use <b>-lp</b> indentation.</p>
+<p>Use <b>-lpil=s</b> to specify the containers to which <b>-lp</b> applies, or</p>
 
-<p>This list is a string with space-separated items. Each item consists of up to three pieces of information in this order: (1) an optional letter code (2) a required container type, and (3) an optional numeric code.</p>
+<p>use <b>-lpxl=s</b> to specify the containers to which <b>-lp</b> does NOT apply.</p>
 
-<p>The only required piece of information is a container type, which is one of &#39;(&#39;, &#39;[&#39;, or &#39;{&#39;. For example the string</p>
-
-<pre><code>  -lpxl=&#39;[ {&#39;</code></pre>
+<p>Only one of these two flags may be used. Both flags can achieve the same result, but the <b>-lpil=s</b> flag is much easier to describe and use and is recommended. The <b>-lpxl=s</b> flag was the original implementation and is only retained for backwards compatibility.</p>
 
-<p>means do <b>NOT</b> include use -lp formatting within square-bracets or braces. The only unspecified container is &#39;(&#39;, so this string means that only the contents within parens will use -lp indentation.</p>
+<p>This list <b>s</b> for these parametes is a string with space-separated items. Each item consists of up to three pieces of information in this order: (1) an optional letter code (2) a required container type, and (3) an optional numeric code.</p>
 
-<p>An optional numeric code may follow any of the container types to further refine the selection based on container contents. The numeric codes are:</p>
+<p>The only required piece of information is a container type, which is one of &#39;(&#39;, &#39;[&#39;, or &#39;{&#39;. For example the string</p>
 
-<pre><code>  &#39;0&#39; or blank: no check on contents
-  &#39;1&#39; reject -lp unless the contents is a simple list without sublists
-  &#39;2&#39; reject -lp unless the contents is a simple list without sublists, without
-      code blocks, and without ternary operators</code></pre>
+<pre><code>  -lpil=&#39;(&#39;</code></pre>
 
-<p>For example,</p>
+<p>means use -lp formatting only on lists within parentheses, not lists in square-bracets or braces. The same thing could alternatively be specified with</p>
 
-<pre><code>  -lpxl = &#39;[ { (2&#39;</code></pre>
+<pre><code>  -lpxl = &#39;[ {&#39;</code></pre>
 
-<p>means only apply -lp to parenthesized lists which do not contain any sublists, code blocks or ternary expressions.</p>
+<p>which says to exclude lists within square-brackets and braces. So what remains is lists within parentheses.</p>
 
-<p>A third optional item of information which can be given for parens is an alphanumeric letter which is used to limit the selection further depending on the type of token immediately before the paren. The possible letters are currently &#39;k&#39;, &#39;K&#39;, &#39;f&#39;, &#39;F&#39;, &#39;w&#39;, and &#39;W&#39;, with these meanings:</p>
+<p>A second optional item of information which can be given for parentheses is an alphanumeric letter which is used to limit the selection further depending on the type of token immediately before the paren. The possible letters are currently &#39;k&#39;, &#39;K&#39;, &#39;f&#39;, &#39;F&#39;, &#39;w&#39;, and &#39;W&#39;, with these meanings for matching whatever precedes an opening paren:</p>
 
 <pre><code> &#39;k&#39; matches if the previous nonblank token is a perl builtin keyword (such as &#39;if&#39;, &#39;while&#39;),
  &#39;K&#39; matches if &#39;k&#39; does not, meaning that the previous token is not a keyword.
  &#39;w&#39; matches if either &#39;k&#39; or &#39;f&#39; match.
  &#39;W&#39; matches if &#39;w&#39; does not.</code></pre>
 
+<p>For example:</p>
+
+<pre><code>  -lpil = &#39;f(&#39;</code></pre>
+
+<p>means only apply -lp to function calls, and</p>
+
+<pre><code>  -lpil = &#39;w(&#39;</code></pre>
+
+<p>means only apply -lp to parenthesized lists which follow a function or a keyword.</p>
+
+<p>This last example could alternatively be written using the <b>-lpxl=s</b> flag as</p>
+
+<pre><code>  -lpxl = &#39;[ { W(&#39;</code></pre>
+
+<p>which says exclude <b>-lp</b> for lists within square-brackets, braces, and parens NOT preceded by a keyword or function. Clearly, the <b>-lpil=s</b> method is easier to understand.</p>
+
+<p>An optional numeric code may follow any of the container types to further refine the selection based on container contents. The numeric codes are:</p>
+
+<pre><code>  &#39;0&#39; or blank: no check on contents is made
+  &#39;1&#39; exclude B&lt;-lp&gt; unless the contents is a simple list without sublists
+  &#39;2&#39; exclude B&lt;-lp&gt; unless the contents is a simple list without sublists, without
+      code blocks, and without ternary operators</code></pre>
+
 <p>For example,</p>
 
-<pre><code>  -lpxl = &#39;[ { F(2&#39;</code></pre>
+<pre><code>  -lpil = &#39;f(2&#39;</code></pre>
 
-<p>means only apply -lp to parenthesized lists which follow a function call and which do not contain any sublists, code blocks or ternary expressions. The logic of writing these codes is somewhat counter-intuitive because they describe what is not getting the -lp indentation. So the &#39;F&#39; indicates that non-function calls are not getting -lp, or in other words that function calls are getting the -lp indentation.</p>
+<p>means only apply -lp to function call lists which do not contain any sublists, code blocks or ternary expressions.</p>
 
 </dd>
 <dt id="cti-n---closing-token-indentation"><b>-cti=n</b>, <b>--closing-token-indentation</b></dt>
             fixit($i);
         }</code></pre>
 
-<p>Use <b>-nola</b> to not outdent labels.</p>
+<p>Use <b>-nola</b> to not outdent labels. To control line breaks after labels see <a href="#bal-n---break-after-labels-n">&quot;bal=n, --break-after-labels=n&quot;</a>.</p>
 
 </dd>
 <dt id="Outdenting-Keywords"><b>Outdenting Keywords</b></dt>
 
 <pre><code> $width = $col[ $j + $k ] - $col[ $j ];  # -sbt=0
  $width = $col[ $j + $k ] - $col[$j];    # -sbt=1 (default)
- $width = $col[$j + $k] - $col[$j];      # -sbt=2 </code></pre>
+ $width = $col[$j + $k] - $col[$j];      # -sbt=2</code></pre>
 
 <p>Curly braces which do not contain code blocks are controlled by the parameter <b>-bt=n</b> or <b>--brace-tightness=n</b>.</p>
 
 
 <p>The flag <b>-tso</b> causes certain perl token sequences (secret operators) which might be considered to be a single operator to be formatted &quot;tightly&quot; (without spaces). The operators currently modified by this flag are:</p>
 
-<pre><code>     0+  +0  ()x!! ~~&lt;&gt;  ,=&gt;   =( )=  </code></pre>
+<pre><code>     0+  +0  ()x!! ~~&lt;&gt;  ,=&gt;   =( )=</code></pre>
 
 <p>For example the sequence <b>0 +</b>, which converts a string to a number, would be formatted without a space: <b>0+</b> when the <b>-tso</b> flag is set. This flag is off by default.</p>
 
 
 <p>When an opening paren follows a Perl keyword, no space is introduced after the keyword, unless it is (by default) one of these:</p>
 
-<pre><code>   my local our and or xor eq ne if else elsif until unless 
+<pre><code>   my local our and or xor eq ne if else elsif until unless
    while for foreach return switch case given when</code></pre>
 
 <p>These defaults can be modified with two commands:</p>
 
 <p><b>-sfp</b> or <b>--space-function-paren</b></p>
 
-<pre><code>  myfunc( $a, $b, $c );    # default 
+<pre><code>  myfunc( $a, $b, $c );    # default
   myfunc ( $a, $b, $c );   # -sfp</code></pre>
 
 <p>You will probably also want to use the flag <b>-skp</b> (previous item) too.</p>
 
 <p>In the following example some extra space has been inserted on the second line between the two open parens. This extra space is called &quot;logical padding&quot; and is intended to help align similar things vertically in some logical or ternary expressions.</p>
 
-<pre><code>    # perltidy [default formatting] 
+<pre><code>    # perltidy [default formatting]
     $same =
       (      ( $aP eq $bP )
           &amp;&amp; ( $aS eq $bS )
 
 <p>Here is an example involving a ternary operator:</p>
 
-<pre><code>    # perltidy [default formatting] 
+<pre><code>    # perltidy [default formatting]
     $bits =
         $top &gt; 0xffff ? 32
       : $top &gt; 0xff   ? 16
 
 <pre><code>     perltidy -l=80
         $vmsfile =~ s/;[\d\-]*$//
-          ;    # Clip off version number; we can use a newer version as well
-   </code></pre>
+          ;    # Clip off version number; we can use a newer version as well</code></pre>
 
 </dd>
 <dt id="hsc---hanging-side-comments"><b>-hsc</b>, <b>--hanging-side-comments</b></dt>
 
 <pre><code>    @month_of_year = (   # -nsbc
         &#39;Jan&#39;, &#39;Feb&#39;, &#39;Mar&#39;, &#39;Apr&#39;, &#39;May&#39;, &#39;Jun&#39;, &#39;Jul&#39;, &#39;Aug&#39;, &#39;Sep&#39;, &#39;Oct&#39;,
-  
+
         ##  &#39;Dec&#39;, &#39;Nov&#39;
         &#39;Nov&#39;, &#39;Dec&#39;
     );</code></pre>
 
 <p>A pattern which can be useful is:</p>
 
-<pre><code>    -sbcp=^#{2,}[^\s#] </code></pre>
+<pre><code>    -sbcp=^#{2,}[^\s#]</code></pre>
 
 <p>This pattern requires a static block comment to have at least one character which is neither a # nor a space. It allows a line containing only &#39;#&#39; characters to be rejected as a static block comment. Such lines are often used at the start and end of header information in subroutines and should not be separated from the intervening comments, which typically begin with just a single &#39;#&#39;.</p>
 
 
 <p>Additional text may appear on the special comment lines provided that it is separated from the marker by at least one space, as in the above examples.</p>
 
+<p>Any number of code-skipping or format-skipping sections may appear in a file. If an opening code-skipping or format-skipping comment is not followed by a corresponding closing comment, then skipping continues to the end of the file. If a closing code-skipping or format-skipping comment appears in a file but does not follow a corresponding opening comment, then it is treated as an ordinary comment without any special meaning.</p>
+
 <p>It is recommended to use <b>--code-skipping</b> only if you need to hide a block of an extended syntax which would produce errors if parsed by perltidy, and use <b>--format-skipping</b> otherwise. This is because the <b>--format-skipping</b> option provides the benefits of error checking, and there are essentially no limitations on which lines to which it can be applied. The <b>--code-skipping</b> option, on the other hand, does not do error checking and its use is more restrictive because the code which remains, after skipping the marked lines, must be syntactically correct code with balanced containers.</p>
 
 <p>These features should be used sparingly to avoid littering code with markers, but they can be helpful for working around occasional problems.</p>
 
 <pre><code> -fsb=&#39;#\{\{\{&#39; becomes /^#\{\{\{\s/  which matches  #{{{ but not #{{{{
  -fsb=&#39;#\*\*&#39;   becomes /^#\*\*\s/    which matches  #** but not #***
- -fsb=&#39;#\*{2,}&#39; becomes /^#\*{2,}\s/  which matches  #** and #***** </code></pre>
+ -fsb=&#39;#\*{2,}&#39; becomes /^#\*{2,}\s/  which matches  #** and #*****</code></pre>
 
 </dd>
 <dt id="fse-string---format-skipping-end-string"><b>-fse=string</b>, <b>--format-skipping-end=string</b></dt>
 <pre><code>  # -ce
   if ($task) {
       yyy();
-  } else {    
+  } else {
       zzz();
   }
 
   if ($task) {
         yyy();
   }
-  else {    
+  else {
         zzz();
   }</code></pre>
 
 
 <p>or equivalently</p>
 
-<pre><code>  -cbl=sort,map,grep </code></pre>
+<pre><code>  -cbl=sort,map,grep</code></pre>
 
 <p>Note however that these particular block types are typically short so there might not be much opportunity for the cuddled format style.</p>
 
 <p>Cuddled formatting is only possible between a pair of code blocks if the closing brace of the first block starts a new line. If a block is encountered which is entirely on a single line, and cuddled formatting is selected, it is necessary to make a decision as to whether or not to &quot;break&quot; the block, meaning to cause it to span multiple lines. This parameter controls that decision. The options are:</p>
 
 <pre><code>   cbo=0  Never force a short block to break.
-   cbo=1  If the first of a pair of blocks is broken in the input file, 
+   cbo=1  If the first of a pair of blocks is broken in the input file,
           then break the second [DEFAULT].
    cbo=2  Break open all blocks for maximal cuddled formatting.</code></pre>
 
 <p>The option <b>cbo=2</b> produces maximal cuddling but will not allow any short blocks.</p>
 
 </dd>
-<dt id="bl---opening-brace-on-new-line"><b>-bl</b>, <b>--opening-brace-on-new-line</b></dt>
+<dt id="bl---opening-brace-on-new-line-or---brace-left"><b>-bl</b>, <b>--opening-brace-on-new-line</b>, or <b>--brace-left</b></dt>
 <dd>
 
-<p>Use the flag <b>-bl</b> to place the opening brace on a new line:</p>
+<p>Use the flag <b>-bl</b> to place an opening block brace on a new line:</p>
+
+<pre><code>  if ( $input_file eq &#39;-&#39; )
+  {
+      ...
+  }</code></pre>
+
+<p>By default it applies to all structural blocks except <b>sort map grep eval</b> and anonymous subs.</p>
+
+<p>The default is <b>-nbl</b> which places an opening brace on the same line as the keyword introducing it if possible. For example,</p>
 
-<pre><code>  if ( $input_file eq &#39;-&#39; )    # -bl 
-  {                          
-      important_function();
+<pre><code>  # default
+  if ( $input_file eq &#39;-&#39; ) {
+     ...
   }</code></pre>
 
-<p>This flag applies to all structural blocks, including named sub&#39;s (unless the <b>-sbl</b> flag is set -- see next item).</p>
+<p>When <b>-bl</b> is set, the blocks to which this applies can be controlled with the parameters <b>--brace-left-list</b> and <b>-brace-left-exclusion-list</b> described in the next sections.</p>
 
-<p>The default style, <b>-nbl</b>, places an opening brace on the same line as the keyword introducing it. For example,</p>
+</dd>
+<dt id="bll-s---brace-left-list-s"><b>-bll=s</b>, <b>--brace-left-list=s</b></dt>
+<dd>
 
-<pre><code>  if ( $input_file eq &#39;-&#39; ) {   # -nbl (default)</code></pre>
+<p>Use this parameter to change the types of block braces for which the <b>-bl</b> flag applies; see <a href="#Specifying-Block-Types">&quot;Specifying Block Types&quot;</a>. For example, <b>-bll=&#39;if elsif else sub&#39;</b> would apply it to only <code>if/elsif/else</code> and named sub blocks. The default is all blocks, <b>-bll=&#39;*&#39;</b>.</p>
+
+</dd>
+<dt id="blxl-s---brace-left-exclusion-list-s"><b>-blxl=s</b>, <b>--brace-left-exclusion-list=s</b></dt>
+<dd>
+
+<p>Use this parameter to exclude types of block braces for which the <b>-bl</b> flag applies; see <a href="#Specifying-Block-Types">&quot;Specifying Block Types&quot;</a>. For example, the default settings <b>-bll=&#39;*&#39;</b> and <b>-blxl=&#39;sort map grep eval asub&#39;</b> mean all blocks except <b>sort map grep eval</b> and anonymous sub blocks.</p>
+
+<p>Note that the lists <b>-bll=s</b> and <b>-blxl=s</b> control the behavior of the <b>-bl</b> flag but have no effect unless the <b>-bl</b> flag is set.</p>
 
 </dd>
 <dt id="sbl---opening-sub-brace-on-new-line"><b>-sbl</b>, <b>--opening-sub-brace-on-new-line</b></dt>
 <dd>
 
-<p>The flag <b>-sbl</b> can be used to override the value of <b>-bl</b> for the opening braces of named sub&#39;s. For example,</p>
+<p>The flag <b>-sbl</b> provides a shortcut way to turn on <b>-bl</b> just for named subs. The same effect can be achieved by turning on <b>-bl</b> with the block list set as <b>-bll=&#39;sub&#39;</b>.</p>
+
+<p>For example,</p>
 
 <pre><code> perltidy -sbl</code></pre>
 
     }
  }</code></pre>
 
-<p>This flag is negated with <b>-nsbl</b>. If <b>-sbl</b> is not specified, the value of <b>-bl</b> is used.</p>
+<p>This flag is negated with <b>-nsbl</b>, which is the default.</p>
 
 </dd>
 <dt id="asbl---opening-anonymous-sub-brace-on-new-line"><b>-asbl</b>, <b>--opening-anonymous-sub-brace-on-new-line</b></dt>
 <dt id="bli---brace-left-and-indent"><b>-bli</b>, <b>--brace-left-and-indent</b></dt>
 <dd>
 
-<p>The flag <b>-bli</b> is the same as <b>-bl</b> but in addition it causes one unit of continuation indentation ( see <b>-ci</b> ) to be placed before an opening and closing block braces.</p>
+<p>The flag <b>-bli</b> is similar to the <b>-bl</b> flag but in addition it causes one unit of continuation indentation ( see <b>-ci</b> ) to be placed before an opening and closing block braces.</p>
 
-<p>For example,</p>
+<p>For example, perltidy -bli gives</p>
 
-<pre><code>        if ( $input_file eq &#39;-&#39; )    # -bli
+<pre><code>        if ( $input_file eq &#39;-&#39; )
           {
             important_function();
           }</code></pre>
 
-<p>By default, this extra indentation occurs for blocks of type: <b>if</b>, <b>elsif</b>, <b>else</b>, <b>unless</b>, <b>for</b>, <b>foreach</b>, <b>sub</b>, <b>while</b>, <b>until</b>, and also with a preceding label. The next item shows how to change this.</p>
+<p>By default, this extra indentation occurs for block types: <b>if</b>, <b>elsif</b>, <b>else</b>, <b>unless</b>, <b>while</b>, <b>for</b>, <b>foreach</b>, <b>do</b>, and also <b>named subs</b> and blocks preceded by a <b>label</b>. The next item shows how to change this.</p>
+
+<p><b>Note</b>: The <b>-bli</b> flag is similar to the <b>-bl</b> flag, with the difference being that braces get indented. But these two flags are implemented independently, and have different default settings for historical reasons. If desired, a mixture of effects can be achieved if desired by turning them both on with different <b>-list</b> settings. In the event that both settings are selected for a certain block type, the <b>-bli</b> style has priority.</p>
 
 </dd>
 <dt id="blil-s---brace-left-and-indent-list-s"><b>-blil=s</b>, <b>--brace-left-and-indent-list=s</b></dt>
 <dd>
 
-<p>Use this parameter to change the types of block braces for which the <b>-bli</b> flag applies; see <a href="#Specifying-Block-Types">&quot;Specifying Block Types&quot;</a>. For example, <b>-blil=&#39;if elsif else&#39;</b> would apply it to only <code>if/elsif/else</code> blocks.</p>
+<p>Use this parameter to change the types of block braces for which the <b>-bli</b> flag applies; see <a href="#Specifying-Block-Types">&quot;Specifying Block Types&quot;</a>.</p>
+
+<p>The default is <b>-blil=&#39;if else elsif unless while for foreach do : sub&#39;</b>.</p>
+
+</dd>
+<dt id="blixl-s---brace-left-and-indent-exclusion-list-s"><b>-blixl=s</b>, <b>--brace-left-and-indent-exclusion-list=s</b></dt>
+<dd>
+
+<p>Use this parameter to exclude types of block braces for which the <b>-bli</b> flag applies; see <a href="#Specifying-Block-Types">&quot;Specifying Block Types&quot;</a>.</p>
+
+<p>This might be useful in conjunction with selecting all blocks <b>-blil=&#39;*&#39;</b>. The default setting is <b>-blixl=&#39; &#39;</b>, which does not exclude any blocks.</p>
+
+<p>Note that the two parameters <b>-blil</b> and <b>-blixl</b> control the behavior of the <b>-bli</b> flag but have no effect unless the <b>-bli</b> flag is set.</p>
 
 </dd>
 <dt id="bar---opening-brace-always-on-right"><b>-bar</b>, <b>--opening-brace-always-on-right</b></dt>
 <pre><code>        # default formatting
         do {
             {
-                next if $x == $y;    
+                next if $x == $y;
             }
         } until $x++ &gt; $z;
 
 
 <p>For example, compare</p>
 
-<pre><code>        # perltidy -wn 
+<pre><code>        # perltidy -wn
         if ( defined( $_Cgi_Query{
             $Config{&#39;methods&#39;}{&#39;authentication&#39;}{&#39;remote&#39;}{&#39;cgi&#39;}{&#39;username&#39;}
         } ) )</code></pre>
 
 <li><p>Opening tokens (except for block braces) are controlled by <b>-vt=n</b>, or <b>--vertical-tightness=n</b>, where</p>
 
-<pre><code> -vt=0 always break a line after opening token (default). 
- -vt=1 do not break unless this would produce more than one 
+<pre><code> -vt=0 always break a line after opening token (default).
+ -vt=1 do not break unless this would produce more than one
          step in indentation in a line.
  -vt=2 never break a line after opening token</code></pre>
 
 </li>
 <li><p>Closing tokens (except for block braces) are controlled by <b>-vtc=n</b>, or <b>--vertical-tightness-closing=n</b>, where</p>
 
-<pre><code> -vtc=0 always break a line before a closing token (default), 
- -vtc=1 do not break before a closing token which is followed 
-        by a semicolon or another closing token, and is not in 
+<pre><code> -vtc=0 always break a line before a closing token (default),
+ -vtc=1 do not break before a closing token which is followed
+        by a semicolon or another closing token, and is not in
         a list environment.
  -vtc=2 never break before a closing token.
  -vtc=3 Like -vtc=1 except always break before a closing token
 
 <p>The difference between <b>-vt=1</b> and <b>-vt=2</b> is shown here:</p>
 
-<pre><code>    # perltidy -lp -vt=1 
+<pre><code>    # perltidy -lp -vt=1
     $init-&gt;add(
                 mysprintf( &quot;(void)find_threadsv(%s);&quot;,
                            cstring( $threadsv_names[ $op-&gt;targ ] )
                 )
     );
 
-    # perltidy -lp -vt=2 
+    # perltidy -lp -vt=2
     $init-&gt;add( mysprintf( &quot;(void)find_threadsv(%s);&quot;,
                            cstring( $threadsv_names[ $op-&gt;targ ] )
                 )
 
 <p>The <b>-bbvt=n</b> flag is just like the <b>-vt=n</b> flag but applies to opening code block braces.</p>
 
-<pre><code> -bbvt=0 break after opening block brace (default). 
- -bbvt=1 do not break unless this would produce more than one 
+<pre><code> -bbvt=0 break after opening block brace (default).
+ -bbvt=1 do not break unless this would produce more than one
          step in indentation in a line.
  -bbvt=2 do not break after opening block brace.</code></pre>
 
 
 <p>The -baao sets the default to be to break after all of the following operators:</p>
 
-<pre><code>    % + - * / x != == &gt;= &lt;= =~ !~ &lt; &gt; | &amp; 
+<pre><code>    % + - * / x != == &gt;= &lt;= =~ !~ &lt; &gt; | &amp;
     = **= += *= &amp;= &lt;&lt;= &amp;&amp;= -= /= |= &gt;&gt;= ||= //= .= %= ^= x=
     . : ? &amp;&amp; || and or err xor</code></pre>
 
 <p>and the <b>-bbao</b> flag sets the default to break before all of these operators. These can be used to define an initial break preference which can be fine-tuned with the <b>-wba</b> and <b>-wbb</b> flags. For example, to break before all operators except an <b>=</b> one could use --bbao -wba=&#39;=&#39; rather than listing every single perl operator except <b>=</b> on a -wbb flag.</p>
 
+</dd>
+<dt id="bal-n---break-after-labels-n"><b>bal=n, --break-after-labels=n</b></dt>
+<dd>
+
+<p>This flag controls whether or not a line break occurs after a label. There are three possible valuse for <b>n</b>:</p>
+
+<pre><code>  -bal=0  break if there is a break in the input [DEFAULt]
+  -bal=1  always break after a label
+  -bal=2  never break after a label</code></pre>
+
+<p>For example,</p>
+
+<pre><code>      # perltidy -bal=1
+      RETURN:
+        return;
+
+      # perltidy -bal=2
+      RETURN: return;</code></pre>
+
 </dd>
 </dl>
 
 <pre><code>    # perltidy (default)
     my @list = ( 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, );</code></pre>
 
-<p>This formatting loses important information. If we place a side comment on one of the lines, for example, we get the following result with with default formatting parameters:</p>
+<p>This formatting loses the nice structure. If we place a side comment anywhere between the opening and closing parens, the original line break points are retained. For example,</p>
 
 <pre><code>    my @list = (
-        1,    # a side comment, comment, or blank keeps list intact
+        1,    # a side comment forces the original line breakpoints to be kept
         1, 1,
         1, 2, 1,
         1, 3, 3, 1,
         1, 4, 6, 4, 1,
     );</code></pre>
 
-<p>We could achieve the same result with a blank line or full comment anywhere between the opening and closing parens.</p>
+<p>The side comment can be a single hash symbol without any text. We could achieve the same result with a blank line or full comment anywhere between the opening and closing parens. Vertical alignment of the list items will still occur if possible.</p>
 
 <p>For another possibility see the -fs flag in <a href="#Skipping-Selected-Sections-of-Code">&quot;Skipping Selected Sections of Code&quot;</a>.</p>
 
                 1, 3, 3, 1,
                 1, 4, 6, 4, 1,);</code></pre>
 
-<p>A disadvantage of this flag is that all tables in the file must already be nicely formatted.</p>
+<p>A disadvantage of this flag compared to the methods discussed above is that all tables in the file must already be nicely formatted.</p>
 
 </dd>
 <dt id="mft-n---maximum-fields-per-table-n"><b>-mft=n</b>, <b>--maximum-fields-per-table=n</b></dt>
 <p>If the computed number of fields for any table exceeds <b>n</b>, then it will be reduced to <b>n</b>. The default value for <b>n</b> is a large number, 40. While this value should probably be left unchanged as a general rule, it might be used on a small section of code to force a list to have a particular number of fields per line, and then either the <b>-boc</b> flag could be used to retain this formatting, or a single comment could be introduced somewhere to freeze the formatting in future applications of perltidy.</p>
 
 <pre><code>    # perltidy -mft=2
-    @month_of_year = (    
+    @month_of_year = (
         &#39;Jan&#39;, &#39;Feb&#39;,
         &#39;Mar&#39;, &#39;Apr&#39;,
         &#39;May&#39;, &#39;Jun&#39;,
 
 <p>A comma which follows a comma arrow, &#39;=&gt;&#39;, is given special consideration. In a long list, it is common to break at all such commas. This parameter can be used to control how perltidy breaks at these commas. (However, it will have no effect if old comma breaks are being forced because <b>-boc</b> is used). The possible values of <b>n</b> are:</p>
 
-<pre><code> n=0 break at all commas after =&gt;  
+<pre><code> n=0 break at all commas after =&gt;
  n=1 stable: break at all commas after =&gt; if container is open,
      EXCEPT FOR one-line containers
  n=2 break at all commas after =&gt;, BUT try to form the maximum
      one-line container lengths
- n=3 do not treat commas after =&gt; specially at all 
+ n=3 do not treat commas after =&gt; specially at all
  n=4 break everything: like n=0 but ALSO break a short container with
      a =&gt; not followed by a comma when -vt=0 is used
  n=5 stable: like n=1 but ALSO break at open one-line containers when
 
 <p>For example, given this snippet:</p>
 
-<pre><code>    return unless $cmd = $cmd || ($dot 
+<pre><code>    return unless $cmd = $cmd || ($dot
         &amp;&amp; $Last_Shell) || &amp;prompt(&#39;|&#39;);
 
     # perltidy -bol [default]
 <dt id="Keeping-old-breakpoints-at-specific-token-types"><b>Keeping old breakpoints at specific token types</b></dt>
 <dd>
 
-<p>Two command line parameters provide detailed control over whether perltidy should keep an old line break before or after a specific token type:</p>
+<p>It is possible to override the choice of line breaks made by perltidy, and force it to follow certain line breaks in the input stream, with these two parameters:</p>
 
 <p><b>-kbb=s</b> or <b>--keep-old-breakpoints-before=s</b>, and</p>
 
         ...;
       };</code></pre>
 
+<p>For the container tokens &#39;{&#39;, &#39;[&#39; and &#39;(&#39; and, their closing counterparts, use the token symbol. Thus, the command to keep a break after all opening parens is:</p>
+
+<pre><code>   perltidy -kba=&#39;(&#39;</code></pre>
+
+<p>It is possible to be more specific in matching parentheses by preceding them with a letter. The possible letters are &#39;k&#39;, &#39;K&#39;, &#39;f&#39;, &#39;F&#39;, &#39;w&#39;, and &#39;W&#39;, with these meanings (these are the same as used in the <b>--weld-nested-exclusion-list</b> and <b>--line-up-parentheses-exclusion-list</b> parameters):</p>
+
+<pre><code> &#39;k&#39; matches if the previous nonblank token is a perl builtin keyword (such as &#39;if&#39;, &#39;while&#39;),
+ &#39;K&#39; matches if &#39;k&#39; does not, meaning that the previous token is not a keyword.
+ &#39;f&#39; matches if the previous token is a function other than a keyword.
+ &#39;F&#39; matches if &#39;f&#39; does not.
+ &#39;w&#39; matches if either &#39;k&#39; or &#39;f&#39; match.
+ &#39;W&#39; matches if &#39;w&#39; does not.</code></pre>
+
+<p>So for example the the following parameter will keep breaks after opening function call parens:</p>
+
+<pre><code>   perltidy -kba=&#39;f(&#39;</code></pre>
+
+<p><b>NOTE</b>: To match all opening curly braces, and no other opening tokens, please prefix the brace it with an asterisk, like this: &#39;*{&#39;. Otherwise a warning message will occur. This is necessary to avoid problems while the input scheme is being updated and generalized. A single bare curly brace previously matched all container tokens, and tentatively still does. Likewise, to match all closing curly braces, and no other closing tokens, use &#39;*}&#39;.</p>
+
 </dd>
 <dt id="iob---ignore-old-breakpoints"><b>-iob</b>, <b>--ignore-old-breakpoints</b></dt>
 <dd>
 
 <p>Use this flag to tell perltidy to ignore existing line breaks to the maximum extent possible. This will tend to produce the longest possible containers, regardless of type, which do not exceed the line length limit. But please note that this parameter has priority over all other parameters requesting that certain old breakpoints be kept.</p>
 
+<p>To illustrate, consider the following input text:</p>
+
+<pre><code>    has subcmds =&gt; (
+        is =&gt; &#39;ro&#39;,
+        default =&gt; sub { [] },
+    );</code></pre>
+
+<p>The default formatting will keep the container broken, giving</p>
+
+<pre><code>    # perltidy [default]
+    has subcmds =&gt; (
+        is      =&gt; &#39;ro&#39;,
+        default =&gt; sub { [] },
+    );</code></pre>
+
+<p>If old breakpoints are ignored, the list will be flattened:</p>
+
+<pre><code>    # perltidy -iob
+    has subcmds =&gt; ( is =&gt; &#39;ro&#39;, default =&gt; sub { [] }, );</code></pre>
+
+<p>Besides flattening lists, this parameter also applies to lines broken at certain logical breakpoints such as &#39;if&#39; and &#39;or&#39;.</p>
+
+<p>Even if this is parameter is not used globally, it provides a convenient way to flatten selected lists from within an editor.</p>
+
 </dd>
 <dt id="kis---keep-interior-semicolons"><b>-kis</b>, <b>--keep-interior-semicolons</b></dt>
 <dd>
 
 <p><b>-kgbl=s</b> or <b>--keyword-group-blanks-list=s</b>, where <b>s</b> is a quoted string, defines the set of keywords which will be formed into groups. The string is a space separated list of keywords. The default set is <b>s=&quot;use require local our my&quot;</b>, but any list of keywords may be used. Comment lines may also be included in a keyword group, even though they are not keywords. To include ordinary block comments, include the symbol <b>BC</b>. To include static block comments (which normally begin with &#39;##&#39;), include the symbol <b>SBC</b>.</p>
 
-<p><b>-kgbs=s</b> or <b>--keyword-group-blanks-size=s</b>, where <b>s</b> is a string describing the number of consecutive keyword statements forming a group. If <b>s</b> is an integer then it is the minimum number required for a group. A maximum value may also be given with the format <b>s=min.max</b>, where <b>min</b> is the minimum number and <b>max</b> is the maximum number, and the min and max values are separated by one or more dots. No groups will be found if the maximum is less than the minimum. The maximum is unlimited if not given. The default is <b>s=5</b>. Some examples:</p>
+<p><b>-kgbs=s</b> or <b>--keyword-group-blanks-size=s</b>, where <b>s</b> is a string describing the number of consecutive keyword statements forming a group (Note: statements separated by blank lines in the input file are considered consecutive for purposes of this count). If <b>s</b> is an integer then it is the minimum number required for a group. A maximum value may also be given with the format <b>s=min.max</b>, where <b>min</b> is the minimum number and <b>max</b> is the maximum number, and the min and max values are separated by one or more dots. No groups will be found if the maximum is less than the minimum. The maximum is unlimited if not given. The default is <b>s=5</b>. Some examples:</p>
 
 <pre><code>    s      min   max         number for group
     3      3     unlimited   3 or more
     1.1    1     1           1
     1..3   1     3           1 to 3
-    1.0    1     0           (no match)
-    </code></pre>
+    1.0    1     0           (no match)</code></pre>
+
+<p>There is no really good default value for this parameter. If it is set too small, then an excessive number of blank lines may be generated. However, some users may prefer reducing the value somewhat below the default, perhaps to <b>s=3</b>.</p>
 
 <p><b>-kgbb=n</b> or <b>--keyword-group-blanks-before=n</b> specifies whether a blank should appear before the first line of the group, as follows:</p>
 
 
 <pre><code>    -lp -bl -noll -pt=2 -bt=2 -sbt=2 -icp</code></pre>
 
+<p>To use this style with <b>-xlp</b> instead of <b>-lp</b> use <b>-gnu -xlp</b>.</p>
+
 </dd>
 <dt id="pbp---perl-best-practices"><b>-pbp</b>, <b>--perl-best-practices</b></dt>
 <dd>
 <p><b>-pbp</b> is an abbreviation for the parameters in the book <b>Perl Best Practices</b> by Damian Conway:</p>
 
 <pre><code>    -l=78 -i=4 -ci=4 -st -se -vt=2 -cti=0 -pt=1 -bt=1 -sbt=1 -bbt=1 -nsfs -nolq
-    -wbb=&quot;% + - * / x != == &gt;= &lt;= =~ !~ &lt; &gt; | &amp; = 
+    -wbb=&quot;% + - * / x != == &gt;= &lt;= =~ !~ &lt; &gt; | &amp; =
           **= += *= &amp;= &lt;&lt;= &amp;&amp;= -= /= |= &gt;&gt;= ||= //= .= %= ^= x=&quot;</code></pre>
 
 <p>Please note that this parameter set includes -st and -se flags, which make perltidy act as a filter on one file only. These can be overridden by placing <b>-nst</b> and/or <b>-nse</b> after the -pbp parameter.</p>
 
 <p>There are a few points to note regarding one-line blocks. A one-line block is something like this,</p>
 
+<pre><code>    if ( -e $file ) { print &quot;&#39;$file&#39; exists\n&quot; }</code></pre>
+
 <p>where the contents within the curly braces is short enough to fit on a single line.</p>
 
 <p>With few exceptions, perltidy retains existing one-line blocks, if it is possible within the line-length constraint, but it does not attempt to form new ones. In other words, perltidy will try to follow the one-line block style of the input file.</p>
 <p>Vertical alignment refers to lining up certain symbols in a list of consecutive similar lines to improve readability. For example, the &quot;fat commas&quot; are aligned in the following statement:</p>
 
 <pre><code>        $data = $pkg-&gt;new(
-            PeerAddr =&gt; join( &quot;.&quot;, @port[ 0 .. 3 ] ),   
+            PeerAddr =&gt; join( &quot;.&quot;, @port[ 0 .. 3 ] ),
             PeerPort =&gt; $port[4] * 256 + $port[5],
             Proto    =&gt; &#39;tcp&#39;
         );</code></pre>
 
-<p>Vertical alignment can be completely turned off using <b>-novalign</b>, a flag mainly intended for debugging. However, vertical alignment can be forced to stop and restart by selectively introducing blank lines. For example, a blank has been inserted in the following code to keep somewhat similar things aligned.</p>
+<p>Vertical alignment can be completely turned off using the <b>-novalign</b> flag mentioned below. However, vertical alignment can be forced to stop and restart by selectively introducing blank lines. For example, a blank has been inserted in the following code to keep somewhat similar things aligned.</p>
 
 <pre><code>    %option_range = (
         &#39;format&#39;             =&gt; [ &#39;tidy&#39;, &#39;html&#39;, &#39;user&#39; ],
             Proto=&gt; &#39;tcp&#39;
         );</code></pre>
 
+<dl>
+
+<dt id="Completely-turning-off-vertical-alignment-with--novalign"><b>Completely turning off vertical alignment with -novalign</b></dt>
+<dd>
+
+<p>The default is to use vertical alignment, but bertical alignment can be completely turned of with the <b>-novalign</b> flag.</p>
+
+<p>A lower level of control of vertical alignment is possible with three parameters <b>-vc</b>, <b>-vsc</b>, and <b>-vbc</b>. These independently control alignment of code, side comments and block comments. They are described in the next section.</p>
+
+<p>The parameter <b>-valign</b> is in fact an alias for <b>-vc -vsc -vbc</b>, and its negative <b>-novalign</b> is an alias for <b>-nvc -nvsc -nvbc</b>.</p>
+
+</dd>
+<dt id="Controlling-code-alignment-with---valign-code-or--vc"><b>Controlling code alignment with --valign-code or -vc</b></dt>
+<dd>
+
+<p>The <b>-vc</b> flag enables alignment of code symbols such as <b>=</b>. The default is <b>-vc</b>. For detailed control of which symbols to align, see the <b>-valign-exclude-list</b> parameter below.</p>
+
+</dd>
+<dt id="Controlling-side-comment-alignment-with---valign-side-comments-or--vsc"><b>Controlling side comment alignment with --valign-side-comments or -vsc</b></dt>
+<dd>
+
+<p>The <b>-vsc</b> flag enables alignment of side comments and is enabled by default. If side comment aligment is disabled with <b>-nvsc</b> they will appear at a fixed space from the preceding code token. The default is <b>-vsc</b></p>
+
+</dd>
+<dt id="Controlling-block-comment-alignment-with---valign-block-comments-or--vbc"><b>Controlling block comment alignment with --valign-block-comments or -vbc</b></dt>
+<dd>
+
+<p>When <b>-vbc</b> is enabled, block comments can become aligned for example if one comment of a consecutive sequence of comments becomes outdented due a length in excess of the maximum line length. If this occurs, the entire group of comments will remain aligned and be outdented by the same amount. This coordinated alignment will not occur if <b>-nvbc</b> is set. The default is <b>-vbc</b>.</p>
+
+</dd>
+<dt id="Finer-alignment-control-with---valign-exclusion-list-s-or--vxl-s-and---valign-inclusion-list-s-or--vil-s"><b>Finer alignment control with --valign-exclusion-list=s or -vxl=s and --valign-inclusion-list=s or -vil=s</b></dt>
+<dd>
+
+<p>More detailed control of alignment types is available with these two parameters. Most of the vertical alignments in typical programs occur at one of the tokens &#39;,&#39;, &#39;=&#39;, and &#39;=&gt;&#39;, but many other alignments are possible and are given in the following list:</p>
+
+<pre><code>  = **= += *= &amp;= &lt;&lt;= &amp;&amp;= -= /= |= &gt;&gt;= ||= //= .= %= ^= x=
+  { ( ? : , ; =&gt; &amp;&amp; || ~~ !~~ =~ !~ // &lt;=&gt; -&gt;
+  if unless and or err for foreach while until</code></pre>
+
+<p>These alignments are all enabled by default, but they can be selectively disabled by including one or more of these tokens in the space-separated list <b>valign-exclusion-list=s</b>. For example, the following would prevent alignment at <b>=</b> and <b>if</b>:</p>
+
+<pre><code>  --valign-exclusion-list=&#39;= if&#39;</code></pre>
+
+<p>If it is simpler to specify only the token types which are to be aligned, then include the types which are to be aligned in the list of <b>--valign-inclusion-list</b>. You may leave the <b>valign-exclusion-list</b> undefined, or use the special symbol <b>*</b> for the exclusion list. For example, the following parameters enable alignment only at commas and &#39;fat commas&#39;:</p>
+
+<pre><code>  --valign-inclusion-list=&#39;, =&gt;&#39;
+  --valign-exclusion-list=&#39;*&#39;     ( this is optional and may be omitted )</code></pre>
+
+<p>These parameter lists should consist of space-separated tokens from the above list of possible alignment tokens, or a &#39;*&#39;. If an unrecognized token appears, it is simply ignored. And if a specific token is entered in both lists by mistake then the exclusion list has priority.</p>
+
+<p>The default values of these parameters enable all alignments and are equivalent to</p>
+
+<pre><code>  --valign-exclusion-list=&#39; &#39;
+  --valign-inclusion-list=&#39;*&#39;</code></pre>
+
+<p>To illustrate, consider the following snippet with default formatting</p>
+
+<pre><code>    # perltidy
+    $co_description = ($color) ? &#39;bold cyan&#39;  : &#39;&#39;;           # description
+    $co_prompt      = ($color) ? &#39;bold green&#39; : &#39;&#39;;           # prompt
+    $co_unused      = ($color) ? &#39;on_green&#39;   : &#39;reverse&#39;;    # unused</code></pre>
+
+<p>To exclude all alignments except the equals (i.e., include only equals) we could use:</p>
+
+<pre><code>    # perltidy -vil=&#39;=&#39;
+    $co_description = ($color) ? &#39;bold cyan&#39; : &#39;&#39;;          # description
+    $co_prompt      = ($color) ? &#39;bold green&#39; : &#39;&#39;;         # prompt
+    $co_unused      = ($color) ? &#39;on_green&#39; : &#39;reverse&#39;;    # unused</code></pre>
+
+<p>To exclude only the equals we could use:</p>
+
+<pre><code>    # perltidy -vxl=&#39;=&#39;
+    $co_description = ($color) ? &#39;bold cyan&#39; : &#39;&#39;;     # description
+    $co_prompt = ($color) ? &#39;bold green&#39; : &#39;&#39;;         # prompt
+    $co_unused = ($color) ? &#39;on_green&#39; : &#39;reverse&#39;;    # unused</code></pre>
+
+<p>Notice in this last example that although only the equals alignment was excluded, the ternary alignments were also lost. This happens because the vertical aligner sweeps from left-to-right and usually stops if an important alignment cannot be made for some reason.</p>
+
+<p>But also notice that side comments remain aligned because their alignment is controlled separately with the parameter <b>--valign-side_comments</b> described above.</p>
+
+</dd>
+</dl>
+
 <h2 id="Other-Controls">Other Controls</h2>
 
 <dl>
 
 <p>Most of the flags accepted by pod2html may be included in the perltidy command line, and they will be passed to pod2html. In some cases, the flags have a prefix <code>pod</code> to emphasize that they are for the pod2html, and this prefix will be removed before they are passed to pod2html. The flags which have the additional <code>pod</code> prefix are:</p>
 
-<pre><code>   --[no]podheader --[no]podindex --[no]podrecurse --[no]podquiet 
+<pre><code>   --[no]podheader --[no]podindex --[no]podrecurse --[no]podquiet
    --[no]podverbose --podflush</code></pre>
 
 <p>The flags which are unchanged from their use in pod2html are:</p>
 
 <pre><code>   --backlink=s --cachedir=s --htmlroot=s --libpods=s --title=s
-   --podpath=s --podroot=s </code></pre>
+   --podpath=s --podroot=s</code></pre>
 
 <p>where &#39;s&#39; is an appropriate character string. Not all of these flags are available in older versions of Pod::Html. See your Pod::Html documentation for more information.</p>
 
 
 <p>Syntax colors may be changed from their default values by flags of the either the long form, <b>-html-color-xxxxxx=n</b>, or more conveniently the short form, <b>-hcx=n</b>, where <b>xxxxxx</b> is one of the following words, and <b>x</b> is the corresponding abbreviation:</p>
 
-<pre><code>      Token Type             xxxxxx           x 
+<pre><code>      Token Type             xxxxxx           x
       ----------             --------         --
       comment                comment          c
       number                 numeric          n
  baao   bar    bbao   bbb    bbc    bbs    bl     bli    boa    boc
  bok    bol    bom    bos    bot    cblx   ce     conv   cs     csc
  cscb   cscw   dac    dbc    dcbl   dcsc   ddf    dln    dnl    dop
- dp     dpro   dsc    dsm    dsn    dtt    dwls   dwrs   dws    f
- fll    fpva   frm    fs     fso    gcs    hbc    hbcm   hbco   hbh
- hbhh   hbi    hbj    hbk    hbm    hbn    hbp    hbpd   hbpu   hbq
- hbs    hbsc   hbv    hbw    hent   hic    hicm   hico   hih    hihh
- hii    hij    hik    him    hin    hip    hipd   hipu   hiq    his
- hisc   hiv    hiw    hsc    html   ibc    icb    icp    iob    isbc
- iscl   kgb    kgbd   kgbi   kis    lal    log    lop    lp     lsl
- mem    nib    ohbr   okw    ola    olc    oll    olq    opr    opt
- osbc   osbr   otr    ple    pod    pvl    q      sac    sbc    sbl
- scbb   schb   scp    scsb   sct    se     sfp    sfs    skp    sob
- sobb   sohb   sop    sosb   sot    ssc    st     sts    t      tac
- tbc    toc    tp     tqw    trp    ts     tsc    tso    vmll   w
wn     x      xci    xs</code></pre>
+ dp     dpro   dsc    dsm    dsn    dtt    dwls   dwrs   dws    eos
+ f      fll    fpva   frm    fs     fso    gcs    hbc    hbcm   hbco
+ hbh    hbhh   hbi    hbj    hbk    hbm    hbn    hbp    hbpd   hbpu
+ hbq    hbs    hbsc   hbv    hbw    hent   hic    hicm   hico   hih
+ hihh   hii    hij    hik    him    hin    hip    hipd   hipu   hiq
+ his    hisc   hiv    hiw    hsc    html   ibc    icb    icp    iob
+ isbc   iscl   kgb    kgbd   kgbi   kis    lal    log    lop    lp
+ lsl    mem    nib    ohbr   okw    ola    olc    oll    olq    opr
+ opt    osbc   osbr   otr    ple    pod    pvl    q      sac    sbc
+ sbl    scbb   schb   scp    scsb   sct    se     sfp    sfs    skp
+ sob    sobb   sohb   sop    sosb   sot    ssc    st     sts    t
+ tac    tbc    toc    tp     tqw    trp    ts     tsc    tso    vbc
vc     vmll   vsc    w      wn     x      xci    xlp    xs</code></pre>
 
 <p>Equivalently, the prefix &#39;no&#39; or &#39;no-&#39; on the corresponding long names may be used.</p>
 
 
 <h1 id="VERSION">VERSION</h1>
 
-<p>This man page documents perltidy version 20210717</p>
+<p>This man page documents perltidy version 20220217</p>
 
 <h1 id="BUG-REPORTS">BUG REPORTS</h1>
 
 
 <h1 id="COPYRIGHT">COPYRIGHT</h1>
 
-<p>Copyright (c) 2000-2021 by Steve Hancock</p>
+<p>Copyright (c) 2000-2022 by Steve Hancock</p>
 
 <h1 id="LICENSE">LICENSE</h1>
 
diff --git a/examples/fix-scbb-csc-bug.pl b/examples/fix-scbb-csc-bug.pl
deleted file mode 100755 (executable)
index dd9c380..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/perl -w
-use strict;
-use warnings;
-
-# This is a script which can try to fix a formatting problem which could have
-# been introduced by perltidy if certain versions of perltidy were run with the
-# particular parameter combination -scbb -csc.  
-
-# The problem occurred in versions 20200110, 20200619, and 20200822 when the
-# parameter combination -scbb -csc was used.  
-
-# This seems to be a fairly rare combination but could certainly happen.  The
-# problem was found during random testing of perltidy.  It is fixed in the latest
-# version.
-
-# What happened is that two consecutive lines which had closing braces
-# and side comments generated by the -csc parameter were missing a
-# separating newline.  So for example the following two lines:
-
-#   } ## end if (...
-# } ## end while (<STYLES>...
-
-# were actually combined like this:
-#   } ## end if (...} ## end while (<STYLES>...
-
-# If this happened to your script you could insert the line breaks by hand.  An
-# alternative is to run this script on the bad file. It runs as a filter and
-# looks for the special patterns and inserts the missing newlines.
-
-# This will probably work on a script which has just been run once with these
-# parameters. But it will probably not work if the script has been reformatted
-# with these parameters multiple times, or if iterations have been done.
-# Unfortunately in that case key comment information will have been lost.
-
-# The script can be modified if a special side comment prefix other than '##
-# end' was used.
-
-# usage:
-#   fix-scbb-csc-bug.pl <infile >ofile
-
-# This is what we are looking for: a closing brace followed by csc prefix
-my $pattern = '} ## end';
-
-while ( my $line = <> ) {
-    chomp $line;
-
-    if ( $line && $line =~ /$pattern/ ) {
-
-        my $leading_spaces = "";
-        my $text;
-        if ( $line =~ /^(\s*)(.*)$/ ) { $leading_spaces = $1; $text = $2 }
-        my @parts = split /$pattern/, $text;
-
-        # just print the line for an exact match
-        if ( !@parts ) { print $line, "\n"; next }
-
-        my $csc     = "";
-        my $braces  = "";
-        my @lines;
-        while ( @parts > 1 ) {
-
-            # Start at the end and work back, saving lines in @lines
-            # If we see something with trailing braces, like } ## end }}
-            # then we will break before the trailing braces.
-            my $part = pop(@parts);
-            $csc    = $part;
-            $braces = "";
-
-            # it's easiest to reverse the string, match multiple braces, and
-            # reverse again
-            my $rev = reverse $part;
-            if ( $rev =~ /^([\}\s]+)(.*)$/ ) {
-                $csc    = reverse $2;
-                $braces = reverse $1;
-            }
-            push @lines, $pattern . $csc;
-            push @lines, $braces if ($braces);
-        }
-
-        # The first section needs leading whitespace
-        if (@parts) {
-            my $part = pop(@parts);
-            if ($part) {
-                my $line = $leading_spaces . $part;
-                push @lines, $line;
-            }
-            elsif (@lines) {
-                my $i = -1;
-                if ($braces) { $i = -2 }
-                $lines[$i] = $leading_spaces . $lines[$i];
-            }
-        }
-        while ( my $line = shift @lines ) {
-            print $line . "\n";
-        }
-        next;
-    }
-    print $line. "\n";
-}
index ffeb8b80c2cde56bc19b01249146f9b32ce1b3c7..a97b7aad43603081bbf509ae4c19ea88806828a5 100644 (file)
@@ -3,7 +3,7 @@
 #
 #    perltidy - a perl script indenter and formatter
 #
-#    Copyright (c) 2000-2021 by Steve Hancock
+#    Copyright (c) 2000-2022 by Steve Hancock
 #    Distributed under the GPL license agreement; see file COPYING
 #
 #    This program is free software; you can redistribute it and/or modify
@@ -110,7 +110,7 @@ BEGIN {
     # Release version must be bumped, and it is probably past time for a
     # release anyway.
 
-    $VERSION = '20210717';
+    $VERSION = '20220217';
 }
 
 sub DESTROY {
@@ -777,6 +777,7 @@ EOM
 
     Perl::Tidy::Formatter::check_options($rOpts);
     Perl::Tidy::Tokenizer::check_options($rOpts);
+    Perl::Tidy::VerticalAligner::check_options($rOpts);
     if ( $rOpts->{'format'} eq 'html' ) {
         Perl::Tidy::HtmlWriter->check_options($rOpts);
     }
@@ -1019,6 +1020,7 @@ EOM
         my $encoding_in              = "";
         my $rOpts_character_encoding = $rOpts->{'character-encoding'};
         my $encoding_log_message;
+        my $decoded_input_as = "";
 
         # Case 1. See if we already have an encoded string. In that
         # case, we have to ignore any encoding flag.
@@ -1081,9 +1083,13 @@ EOM
                         $encoding_log_message .= <<EOM;
 Guessed encoding '$encoding_in' successfully decoded
 EOM
+                        $decoded_input_as = $encoding_in;
                     }
                 }
             }
+            $encoding_log_message .= <<EOM;
+Unable to guess a character encoding
+EOM
         }
 
         # Case 4. Decode with a specific encoding
@@ -1106,6 +1112,7 @@ EOM
                 $encoding_log_message .= <<EOM;
 Specified encoding '$encoding_in' successfully decoded
 EOM
+                $decoded_input_as = $encoding_in;
             }
         }
 
@@ -1234,6 +1241,7 @@ EOM
             }
         }
         elsif ($destination_stream) {
+
             $output_file = $destination_stream;
         }
         elsif ($source_stream) {    # source but no destination goes to stdout
@@ -1282,6 +1290,20 @@ EOM
           || $rOpts->{'assert-tidy'}
           || $rOpts->{'assert-untidy'};
 
+        # Postpone final output to a destination SCALAR or ARRAY ref to allow
+        # possible encoding at the end of processing.
+        my $destination_buffer;
+        my $use_destination_buffer;
+        if (
+            ref($destination_stream)
+            && (   ref($destination_stream) eq 'SCALAR'
+                || ref($destination_stream) eq 'ARRAY' )
+          )
+        {
+            $use_destination_buffer = 1;
+            $output_file            = \$destination_buffer;
+        }
+
         $sink_object = Perl::Tidy::LineSink->new(
             output_file    => $use_buffer ? \$postfilter_buffer : $output_file,
             line_separator => $line_separator,
@@ -1558,7 +1580,7 @@ EOM
                     last;
                 }
             } ## end if ( $iter < $max_iterations)
-        }    # end loop over iterations for one source file
+        } ## end loop over iterations for one source file
 
         # restore objects which have been temporarily undefined
         # for second and higher iterations
@@ -1644,7 +1666,53 @@ EOM
             $source_object->close_input_file();
         }
 
-        # Save names of the input and output files for syntax check
+        #------------------------------------------------------------------
+        # For string output, store the result to the destination, encoding
+        # if requested. This is a fix for issue git #83 (tidyall issue)
+        #------------------------------------------------------------------
+        if ($use_destination_buffer) {
+
+            # At this point, all necessary encoding has been done except for
+            # output to a string or array ref. We use the -eos flag to decide
+            # if we should encode.
+
+            # -neos, DEFAULT: perltidy does not return encoded string output.
+            # This is a result of the code evolution but not very convenient for
+            # most applications.  It would be hard to change without breaking
+            # some programs.
+
+            # -eos flag set: If perltidy decodes a string, regardless of
+            # source, it encodes before returning.
+
+            if ( $rOpts->{'encode-output-strings'} && $decoded_input_as ) {
+                my $encoded_buffer;
+                eval {
+                    $encoded_buffer =
+                      Encode::encode( "UTF-8", $destination_buffer,
+                        Encode::FB_CROAK | Encode::LEAVE_SRC );
+                };
+                if ($@) {
+
+                    Warn(
+"Error attempting to encode output string ref; encoding not done\n"
+                    );
+                }
+                else {
+                    $destination_buffer = $encoded_buffer;
+                }
+            }
+
+            # Final string storage
+            if ( ref($destination_stream) eq 'SCALAR' ) {
+                ${$destination_stream} = $destination_buffer;
+            }
+            else {
+                my @lines = split /^/, $destination_buffer;
+                @{$destination_stream} = @lines;
+            }
+        }
+
+        # Save names of the input and output files
         my $ifname = $input_file;
         my $ofname = $output_file;
 
@@ -1782,21 +1850,6 @@ EOM
             }
         }
 
-        #---------------------------------------------------------------
-        # Do syntax check if requested and possible
-        # This is permanently deactivated but the code remains for reference
-        #---------------------------------------------------------------
-        my $infile_syntax_ok = 0;    # -1 no  0=don't know   1 yes
-        if (   0
-            && $logger_object
-            && $rOpts->{'check-syntax'}
-            && $ifname
-            && $ofname )
-        {
-            $infile_syntax_ok =
-              check_syntax( $ifname, $ofname, $logger_object, $rOpts );
-        }
-
         #---------------------------------------------------------------
         # remove the original file for in-place modify as follows:
         #   $delete_backup=0 never
@@ -1828,9 +1881,9 @@ EOM
             }
         }
 
-        $logger_object->finish( $infile_syntax_ok, $formatter )
+        $logger_object->finish($formatter)
           if $logger_object;
-    }    # end of main loop to process all files
+    } ## end of main loop to process all files
 
     # Fix for RT #130297: return a true value if anything was written to the
     # standard error output, even non-fatal warning messages, otherwise return
@@ -2007,8 +2060,8 @@ sub get_stream_as_named_file {
     #  $fname = name of file if possible, or undef
     #  $if_tmpfile = true if temp file, undef if not temp file
     #
-    # This routine is needed for passing actual files to Perl for
-    # a syntax check.
+    # NOTE: This routine was previously needed for passing actual files to Perl
+    # for a syntax check. It is not currently used.
     my ($stream) = @_;
     my $is_tmpfile;
     my $fname;
@@ -2125,7 +2178,6 @@ sub generate_options {
     #                                       which is mainly for debugging
     # scl --> short-concatenation-item-length   # helps break at '.'
     # recombine                           # for debugging line breaks
-    # valign                              # for debugging vertical alignment
     # I   --> DIAGNOSTICS                 # for debugging [**DEACTIVATED**]
     ######################################################################
 
@@ -2181,7 +2233,6 @@ sub generate_options {
       no-profile
       npro
       recombine!
-      valign!
       notidy
     );
 
@@ -2220,6 +2271,7 @@ sub generate_options {
                 $expansion{$nshort_name} = [$nolong_name];
             }
         }
+        return;
     };
 
     # Install long option names which have a simple abbreviation.
@@ -2271,26 +2323,32 @@ sub generate_options {
     $add_option->( 'extended-syntax',              'xs',   '!' );
     $add_option->( 'assert-tidy',                  'ast',  '!' );
     $add_option->( 'assert-untidy',                'asu',  '!' );
+    $add_option->( 'encode-output-strings',        'eos',  '!' );
     $add_option->( 'sub-alias-list',               'sal',  '=s' );
+    $add_option->( 'grep-alias-list',              'gal',  '=s' );
+    $add_option->( 'grep-alias-exclusion-list',    'gaxl', '=s' );
 
     ########################################
     $category = 2;    # Code indentation control
     ########################################
-    $add_option->( 'continuation-indentation',           'ci',   '=i' );
-    $add_option->( 'extended-continuation-indentation',  'xci',  '!' );
-    $add_option->( 'line-up-parentheses',                'lp',   '!' );
-    $add_option->( 'line-up-parentheses-exclusion-list', 'lpxl', '=s' );
-    $add_option->( 'outdent-keyword-list',               'okwl', '=s' );
-    $add_option->( 'outdent-keywords',                   'okw',  '!' );
-    $add_option->( 'outdent-labels',                     'ola',  '!' );
-    $add_option->( 'outdent-long-quotes',                'olq',  '!' );
-    $add_option->( 'indent-closing-brace',               'icb',  '!' );
-    $add_option->( 'closing-token-indentation',          'cti',  '=i' );
-    $add_option->( 'closing-paren-indentation',          'cpi',  '=i' );
-    $add_option->( 'closing-brace-indentation',          'cbi',  '=i' );
-    $add_option->( 'closing-square-bracket-indentation', 'csbi', '=i' );
-    $add_option->( 'brace-left-and-indent',              'bli',  '!' );
-    $add_option->( 'brace-left-and-indent-list',         'blil', '=s' );
+    $add_option->( 'continuation-indentation',             'ci',    '=i' );
+    $add_option->( 'extended-continuation-indentation',    'xci',   '!' );
+    $add_option->( 'line-up-parentheses',                  'lp',    '!' );
+    $add_option->( 'extended-line-up-parentheses',         'xlp',   '!' );
+    $add_option->( 'line-up-parentheses-exclusion-list',   'lpxl',  '=s' );
+    $add_option->( 'line-up-parentheses-inclusion-list',   'lpil',  '=s' );
+    $add_option->( 'outdent-keyword-list',                 'okwl',  '=s' );
+    $add_option->( 'outdent-keywords',                     'okw',   '!' );
+    $add_option->( 'outdent-labels',                       'ola',   '!' );
+    $add_option->( 'outdent-long-quotes',                  'olq',   '!' );
+    $add_option->( 'indent-closing-brace',                 'icb',   '!' );
+    $add_option->( 'closing-token-indentation',            'cti',   '=i' );
+    $add_option->( 'closing-paren-indentation',            'cpi',   '=i' );
+    $add_option->( 'closing-brace-indentation',            'cbi',   '=i' );
+    $add_option->( 'closing-square-bracket-indentation',   'csbi',  '=i' );
+    $add_option->( 'brace-left-and-indent',                'bli',   '!' );
+    $add_option->( 'brace-left-and-indent-list',           'blil',  '=s' );
+    $add_option->( 'brace-left-and-indent-exclusion-list', 'blixl', '=s' );
 
     ########################################
     $category = 3;    # Whitespace control
@@ -2323,6 +2381,11 @@ sub generate_options {
     $add_option->( 'want-left-space',                           'wls',   '=s' );
     $add_option->( 'want-right-space',                          'wrs',   '=s' );
     $add_option->( 'space-prototype-paren',                     'spp',   '=i' );
+    $add_option->( 'valign-code',                               'vc',    '!' );
+    $add_option->( 'valign-block-comments',                     'vbc',   '!' );
+    $add_option->( 'valign-side-comments',                      'vsc',   '!' );
+    $add_option->( 'valign-exclusion-list',                     'vxl',   '=s' );
+    $add_option->( 'valign-inclusion-list',                     'vil',   '=s' );
 
     ########################################
     $category = 4;    # Comment controls
@@ -2403,6 +2466,14 @@ sub generate_options {
     $add_option->( 'break-before-square-bracket-and-indent',  'bbsbi', '=i' );
     $add_option->( 'break-before-paren',                      'bbp',   '=i' );
     $add_option->( 'break-before-paren-and-indent',           'bbpi',  '=i' );
+    $add_option->( 'brace-left-list',                         'bll',   '=s' );
+    $add_option->( 'brace-left-exclusion-list',               'blxl',  '=s' );
+    $add_option->( 'break-after-labels',                      'bal',   '=i' );
+
+    ## This was an experiment mentioned in git #78. It works, but it does not
+    ## look very useful.  Instead, I expanded the functionality of the
+    ## --keep-old-breakpoint-xxx flags.
+    ##$add_option->( 'break-open-paren-list',                   'bopl',  '=s' );
 
     ########################################
     $category = 6;    # Controlling list formatting
@@ -2559,13 +2630,14 @@ sub generate_options {
         'keyword-group-blanks-after'  => [ 0, 2 ],
 
         'space-prototype-paren' => [ 0, 2 ],
+        'break-after-labels'    => [ 0, 2 ],
     );
 
     # Note: we could actually allow negative ci if someone really wants it:
     # $option_range{'continuation-indentation'} = [ undef, undef ];
 
     #---------------------------------------------------------------
-    # Assign default values to the above options here, except
+    # DEFAULTS: Assign default values to the above options here, except
     # for 'outfile' and 'help'.
     # These settings should approximate the perlstyle(1) suggestions.
     #---------------------------------------------------------------
@@ -2591,6 +2663,7 @@ sub generate_options {
       brace-tightness=1
       brace-vertical-tightness-closing=0
       brace-vertical-tightness=0
+      break-after-labels=0
       break-at-old-logical-breakpoints
       break-at-old-ternary-breakpoints
       break-at-old-attribute-breakpoints
@@ -2660,7 +2733,9 @@ sub generate_options {
       noweld-nested-containers
       recombine
       nouse-unicode-gcstring
-      valign
+      valign-code
+      valign-block-comments
+      valign-side-comments
       short-concatenation-item-length=8
       space-for-semicolon
       space-backslash-quote=1
@@ -2722,6 +2797,8 @@ sub generate_options {
         'nhtml' => [qw(format=tidy)],
         'tidy'  => [qw(format=tidy)],
 
+        'brace-left' => [qw(opening-brace-on-new-line)],
+
         # -cb is now a synonym for -ce
         'cb'             => [qw(cuddled-else)],
         'cuddled-blocks' => [qw(cuddled-else)],
@@ -2817,6 +2894,9 @@ sub generate_options {
         'conv'       => [qw(it=4)],
         'nconv'      => [qw(it=1)],
 
+        'valign'   => [qw(vc vsc vbc)],
+        'novalign' => [qw(nvc nvsc nvbc)],
+
         # NOTE: This is a possible future shortcut.  But it will remain
         # deactivated until the -lpxl flag is no longer experimental.
         # 'line-up-function-parentheses' => [ qw(lp), q#lpxl=[ { F(2# ],
@@ -2905,7 +2985,7 @@ q(wbb=% + - * / x != == >= <= =~ !~ < > | & = **= += *= &= <<= &&= -= /= |= >>=
         \%option_category, \%option_range
     );
 
-}    # end of generate_options
+} ## end of generate_options
 
 # Memoize process_command_line. Given same @ARGV passed in, return same
 # values and same @ARGV back.
@@ -3204,7 +3284,67 @@ EOM
 
     return ( \%Opts, $config_file, \@raw_options, $roption_string,
         $rexpansion, $roption_category, $roption_range );
-}    # end of _process_command_line
+} ## end of _process_command_line
+
+sub make_grep_alias_string {
+    my ($rOpts) = @_;
+
+    # Defaults: list operators in List::Util
+    # Possible future additions:  pairfirst pairgrep pairmap
+    my $default_string = join ' ', qw(
+      all
+      any
+      first
+      none
+      notall
+      reduce
+      reductions
+    );
+
+    # make a hash of any excluded words
+    my %is_excluded_word;
+    my $exclude_string = $rOpts->{'grep-alias-exclusion-list'};
+    if ($exclude_string) {
+        $exclude_string =~ s/,/ /g;    # allow commas
+        $exclude_string =~ s/^\s+//;
+        $exclude_string =~ s/\s+$//;
+        my @q = split /\s+/, $exclude_string;
+        @is_excluded_word{@q} = (1) x scalar(@q);
+    }
+
+    # The special option -gaxl='*' removes all defaults
+    if ( $is_excluded_word{'*'} ) { $default_string = "" }
+
+    # combine the defaults and any input list
+    my $input_string = $rOpts->{'grep-alias-list'};
+    if ($input_string) { $input_string .= " " . $default_string }
+    else               { $input_string = $default_string }
+
+    # Now make the final list of unique grep alias words
+    $input_string =~ s/,/ /g;    # allow commas
+    $input_string =~ s/^\s+//;
+    $input_string =~ s/\s+$//;
+    my @word_list = split /\s+/, $input_string;
+    my @filtered_word_list;
+    my %seen;
+
+    foreach my $word (@word_list) {
+        if ($word) {
+            if ( $word !~ /^\w[\w\d]*$/ ) {
+                Warn(
+                    "unexpected word in --grep-alias-list: '$word' - ignoring\n"
+                );
+            }
+            if ( !$seen{$word} && !$is_excluded_word{$word} ) {
+                $seen{$word}++;
+                push @filtered_word_list, $word;
+            }
+        }
+    }
+    my $joined_words = join ' ', @filtered_word_list;
+    $rOpts->{'grep-alias-list'} = $joined_words;
+    return;
+}
 
 sub check_options {
 
@@ -3214,6 +3354,15 @@ sub check_options {
     # check and handle any interactions among the basic options..
     #---------------------------------------------------------------
 
+    # Since perltidy only encodes in utf8, problems can occur if we let it
+    # decode anything else.  See discussions for issue git #83.
+    my $encoding = $rOpts->{'character-encoding'};
+    if ( $encoding !~ /^\s*(guess|none|utf8|utf-8)\s*$/i ) {
+        Die(<<EOM);
+--character-encoding = '$encoding' is not allowed; the options are: 'none', 'guess', 'utf8'
+EOM
+    }
+
     # Since -vt, -vtc, and -cti are abbreviations, but under
     # msdos, an unquoted input parameter like vtc=1 will be
     # seen as 2 parameters, vtc and 1, so the abbreviations
@@ -3273,6 +3422,7 @@ sub check_options {
                 $rOpts->{$key} = 100;
             }
         }
+        return;
     };
 
     # check for reasonable number of blank lines and fix to avoid problems
@@ -3321,22 +3471,11 @@ EOM
         }
     }
 
-    # -bli flag implies -bl
-    if ( $rOpts->{'brace-left-and-indent'} ) {
-        $rOpts->{'opening-brace-on-new-line'} = 1;
-    }
-
     # it simplifies things if -bl is 0 rather than undefined
     if ( !defined( $rOpts->{'opening-brace-on-new-line'} ) ) {
         $rOpts->{'opening-brace-on-new-line'} = 0;
     }
 
-    # -sbl defaults to -bl if not defined
-    if ( !defined( $rOpts->{'opening-sub-brace-on-new-line'} ) ) {
-        $rOpts->{'opening-sub-brace-on-new-line'} =
-          $rOpts->{'opening-brace-on-new-line'};
-    }
-
     if ( $rOpts->{'entab-leading-whitespace'} ) {
         if ( $rOpts->{'entab-leading-whitespace'} < 0 ) {
             Warn("-et=n must use a positive integer; ignoring -et\n");
@@ -3387,10 +3526,11 @@ EOM
                 }
             }
         }
-        my $joined_words = join ' ', @filtered_word_list;
         $rOpts->{'sub-alias-list'} = join ' ', @filtered_word_list;
     }
 
+    make_grep_alias_string($rOpts);
+
     # Turn on fuzzy-line-length unless this is an extrude run, as determined
     # by the -i and -ci settings. Otherwise blinkers can form (case b935)
     if ( !$rOpts->{'fuzzy-line-length'} ) {
@@ -3512,7 +3652,7 @@ sub expand_command_abbreviations {
             else {
                 push( @new_argv, $word );
             }
-        }    # end of this pass
+        } ## end of this pass
 
         # update parameter list @ARGV to the new one
         @ARGV = @new_argv;
@@ -3551,8 +3691,8 @@ Program bug - circular-references in the %expansion hash, probably due to
 a recent program change.
 DIE
             }
-        }    # end of check for circular references
-    }    # end of loop over all passes
+        } ## end of check for circular references
+    } ## end of loop over all passes
     return;
 }
 
@@ -4208,7 +4348,7 @@ sub show_version {
     print STDOUT <<"EOM";
 This is perltidy, v$VERSION 
 
-Copyright 2000-2021, Steve Hancock
+Copyright 2000-2022, Steve Hancock
 
 Perltidy is free software and may be copied under the terms of the GNU
 General Public License, which is included in the distribution files.
@@ -4243,7 +4383,6 @@ I/O control
  -bext=s change default backup extension from 'bak' to s
  -q      deactivate error messages (for running under editor)
  -w      include non-critical warning messages in the .ERR error output
- -syn    run perl -c to check syntax (default under unix systems)
  -log    save .LOG file, which has useful diagnostics
  -f      force perltidy to read a binary file
  -g      like -log but writes more detailed .LOG file, for debugging scripts
@@ -4437,110 +4576,4 @@ sub process_this_file {
 
     return;
 }
-
-sub check_syntax {
-
-    # Use 'perl -c' to make sure that we did not create bad syntax
-    # This is a very good independent check for programming errors
-    #
-    # Given names of the input and output files, ($istream, $ostream),
-    # we do the following:
-    # - check syntax of the input file
-    # - if bad, all done (could be an incomplete code snippet)
-    # - if infile syntax ok, then check syntax of the output file;
-    #   - if outfile syntax bad, issue warning; this implies a code bug!
-    # - set and return flag "infile_syntax_ok" : =-1 bad 0 unknown 1 good
-
-    my ( $istream, $ostream, $logger_object, $rOpts ) = @_;
-    my $infile_syntax_ok = 0;
-    my $line_of_dashes   = '-' x 42 . "\n";
-
-    my $flags = $rOpts->{'perl-syntax-check-flags'};
-
-    # be sure we invoke perl with -c
-    # note: perl will accept repeated flags like '-c -c'.  It is safest
-    # to append another -c than try to find an interior bundled c, as
-    # in -Tc, because such a 'c' might be in a quoted string, for example.
-    if ( $flags !~ /(^-c|\s+-c)/ ) { $flags .= " -c" }
-
-    # be sure we invoke perl with -x if requested
-    # same comments about repeated parameters applies
-    if ( $rOpts->{'look-for-hash-bang'} ) {
-        if ( $flags !~ /(^-x|\s+-x)/ ) { $flags .= " -x" }
-    }
-
-    # this shouldn't happen unless a temporary file couldn't be made
-    if ( $istream eq '-' ) {
-        $logger_object->write_logfile_entry(
-            "Cannot run perl -c on STDIN and STDOUT\n");
-        return $infile_syntax_ok;
-    }
-
-    $logger_object->write_logfile_entry(
-        "checking input file syntax with perl $flags\n");
-
-    # Not all operating systems/shells support redirection of the standard
-    # error output.
-    my $error_redirection = ( $^O eq 'VMS' ) ? "" : '2>&1';
-
-    my ( $istream_filename, $perl_output ) =
-      do_syntax_check( $istream, $flags, $error_redirection );
-    $logger_object->write_logfile_entry(
-        "Input stream passed to Perl as file $istream_filename\n");
-    $logger_object->write_logfile_entry($line_of_dashes);
-    $logger_object->write_logfile_entry("$perl_output\n");
-
-    if ( $perl_output =~ /syntax\s*OK/ ) {
-        $infile_syntax_ok = 1;
-        $logger_object->write_logfile_entry($line_of_dashes);
-        $logger_object->write_logfile_entry(
-            "checking output file syntax with perl $flags ...\n");
-        my ( $ostream_filename, $perl_output ) =
-          do_syntax_check( $ostream, $flags, $error_redirection );
-        $logger_object->write_logfile_entry(
-            "Output stream passed to Perl as file $ostream_filename\n");
-        $logger_object->write_logfile_entry($line_of_dashes);
-        $logger_object->write_logfile_entry("$perl_output\n");
-
-        unless ( $perl_output =~ /syntax\s*OK/ ) {
-            $logger_object->write_logfile_entry($line_of_dashes);
-            $logger_object->warning(
-"The output file has a syntax error when tested with perl $flags $ostream !\n"
-            );
-            $logger_object->warning(
-                "This implies an error in perltidy; the file $ostream is bad\n"
-            );
-            $logger_object->report_definite_bug();
-
-            # the perl version number will be helpful for diagnosing the problem
-            $logger_object->write_logfile_entry( $^V . "\n" );
-        }
-    }
-    else {
-
-        # Only warn of perl -c syntax errors.  Other messages,
-        # such as missing modules, are too common.  They can be
-        # seen by running with perltidy -w
-        $logger_object->complain("A syntax check using perl $flags\n");
-        $logger_object->complain(
-            "for the output in file $istream_filename gives:\n");
-        $logger_object->complain($line_of_dashes);
-        $logger_object->complain("$perl_output\n");
-        $logger_object->complain($line_of_dashes);
-        $infile_syntax_ok = -1;
-        $logger_object->write_logfile_entry($line_of_dashes);
-        $logger_object->write_logfile_entry(
-"The output file will not be checked because of input file problems\n"
-        );
-    }
-    return $infile_syntax_ok;
-}
-
-sub do_syntax_check {
-
-    # This should not be called; the syntax check is deactivated
-    Die("Unexpected call for syntax check-shouldn't happen\n");
-    return;
-}
-
 1;
index 138b336e7fea92195d6af2f45c055ca30e125060..0423b7e1ed1597518d8b9b7ff3857e0cf091b0cf 100644 (file)
@@ -49,9 +49,9 @@ either a B<getline> or B<print> method, as appropriate.
         destination       - the destination of the formatted output
         stderr            - standard error output
         perltidyrc        - the .perltidyrc file
-        logfile           - the .LOG file stream, if any 
+        logfile           - the .LOG file stream, if any
         errorfile         - the .ERR file stream, if any
-        dump_options      - ref to a hash to receive parameters (see below), 
+        dump_options      - ref to a hash to receive parameters (see below),
         dump_options_type - controls contents of dump_options
         dump_getopt_flags - ref to a hash to receive Getopt flags
         dump_options_category - ref to a hash giving category of options
@@ -72,65 +72,115 @@ close method will be called at the end of the stream.
 
 =over 4
 
-=item source
+=item B<source>
 
 If the B<source> parameter is given, it defines the source of the input stream.
 If an input stream is defined with the B<source> parameter then no other source
 filenames may be specified in the @ARGV array or B<argv> parameter.
 
-=item destination
+=item B<destination>
 
 If the B<destination> parameter is given, it will be used to define the
-file or memory location to receive output of perltidy.  
+file or memory location to receive output of perltidy.
 
-=item stderr
+B<Important note if destination is a string or array reference>.  Perl strings
+of characters which are decoded as utf8 by Perl::Tidy can be returned in either
+of two possible states, decoded or encoded, and it is important that the
+calling program and Perl::Tidy are in agreement regarding the state to be
+returned.  A flag B<--encode-output-strings>, or simply B<-eos>, was added in
+versions of Perl::Tidy after 20220101 for this purpose. This flag should be
+added to the end of the B<argv> paremeter (described below) if Perl::Tidy
+will be decoding utf8 text.  The options are as follows.
+
+=over 4
+
+=item *
+
+Use B<-eos> if Perl::Tidy should encode any string which it decodes.
+This is probably most convenient for most programs.
+But do not use this setting if the calling program will
+encode the data too, because double encoding will corrupt data.
+
+=item *
+
+Use B<-neos> if a string should remain decoded if it was decoded by Perl::Tidy.
+This is appropriate if
+the calling program will handle any needed encoding before outputting the string.
+
+=item *
+
+The current default is B<-neos>, but B<the default could change in a future
+version>, so B<-neos> should still be set, if appropriate, to allow for the
+possibility of a future change in the default.
+
+=back
+
+For example, to set B<-eos> the following could be used
+
+        $argv .= " -eos" if ( $Perl::Tidy::VERSION > 20220101 );
+
+        $error_flag = Perl::Tidy::perltidy(
+            argv        => $argv,
+            source      => \$source,
+            destination => \$destination,
+            stderr      => \$stderr,
+            errorfile   => \$errorfile
+        );
+
+The test on version allows older versions of Perl::Tidy to still be used.
+
+For some background information see
+L<https://github.com/perltidy/perltidy/issues/83> and
+L<https://github.com/houseabsolute/perl-code-tidyall/issues/84>.
+
+=item B<stderr>
 
 The B<stderr> parameter allows the calling program to redirect the stream that
 would otherwise go to the standard error output device to any of the stream
-types listed above.  This stream contains important warnings and errors 
+types listed above.  This stream contains important warnings and errors
 related to the parameters passed to perltidy.
 
-=item perltidyrc
+=item B<perltidyrc>
 
 If the B<perltidyrc> file is given, it will be used instead of any
-F<.perltidyrc> configuration file that would otherwise be used. 
+F<.perltidyrc> configuration file that would otherwise be used.
 
-=item errorfile
+=item B<errorfile>
 
 The B<errorfile> parameter allows the calling program to capture
 the stream that would otherwise go to either a .ERR file.  This
 stream contains warnings or errors related to the contents of one
-source file or stream. 
+source file or stream.
 
 The reason that this is different from the stderr stream is that when perltidy
 is called to process multiple files there will be up to one .ERR file created
-for each file and it would be very confusing if they were combined.  
+for each file and it would be very confusing if they were combined.
 
 However if perltidy is called to process just a single perl script then it may
 be more convenient to combine the B<errorfile> stream with the B<stderr>
 stream.  This can be done by setting the B<-se> parameter, in which case this
 parameter is ignored.
 
-=item logfile
+=item B<logfile>
 
 The B<logfile> parameter allows the calling program to capture the log stream.
 This stream is only created if requested with a B<-g> parameter.  It contains
 detailed diagnostic information about a script which may be useful for
 debugging.
 
-=item teefile
+=item B<teefile>
 
 The B<teefile> parameter allows the calling program to capture the tee stream.
 This stream is only created if requested with one of the 'tee' parameters,
 a B<--tee-pod> , B<--tee-block-comments>, B<--tee-side-commnts>, or B<--tee-all-comments>.
 
-=item debugfile
+=item B<debugfile>
 
 The B<debugfile> parameter allows the calling program to capture the stream
-produced by the B<--DEBUG> parameter.  This parameter is mainly used for 
+produced by the B<--DEBUG> parameter.  This parameter is mainly used for
 debugging perltidy itself.
 
-=item argv
+=item B<argv>
 
 If the B<argv> parameter is given, it will be used instead of the
 B<@ARGV> array.  The B<argv> parameter may be a string, a reference to a
@@ -138,7 +188,7 @@ string, or a reference to an array.  If it is a string or reference to a
 string, it will be parsed into an array of items just as if it were a
 command line string.
 
-=item dump_options
+=item B<dump_options>
 
 If the B<dump_options> parameter is given, it must be the reference to a hash.
 In this case, the parameters contained in any perltidyrc configuration file
@@ -152,14 +202,14 @@ this feature, F<perltidyrc_dump.pl>, is included in the distribution.
 
 Any combination of the B<dump_> parameters may be used together.
 
-=item dump_options_type
+=item B<dump_options_type>
 
 This parameter is a string which can be used to control the parameters placed
 in the hash reference supplied by B<dump_options>.  The possible values are
 'perltidyrc' (default) and 'full'.  The 'full' parameter causes both the
 default options plus any options found in a perltidyrc file to be returned.
 
-=item dump_getopt_flags
+=item B<dump_getopt_flags>
 
 If the B<dump_getopt_flags> parameter is given, it must be the reference to a
 hash.  This hash will receive all of the parameters that perltidy understands
@@ -168,26 +218,26 @@ used alone or with the B<dump_options> flag.  Perltidy will
 exit immediately after filling this hash.  See the demo program
 F<perltidyrc_dump.pl> for example usage.
 
-=item dump_options_category
+=item B<dump_options_category>
 
 If the B<dump_options_category> parameter is given, it must be the reference to a
 hash.  This hash will receive a hash with keys equal to all long parameter names
 and values equal to the title of the corresponding section of the perltidy manual.
 See the demo program F<perltidyrc_dump.pl> for example usage.
 
-=item dump_abbreviations
+=item B<dump_abbreviations>
 
 If the B<dump_abbreviations> parameter is given, it must be the reference to a
 hash.  This hash will receive all abbreviations used by Perl::Tidy.  See the
 demo program F<perltidyrc_dump.pl> for example usage.
 
-=item prefilter
+=item B<prefilter>
 
 A code reference that will be applied to the source before tidying. It is
 expected to take the full content as a string in its input, and output the
 transformed content.
 
-=item postfilter
+=item B<postfilter>
 
 A code reference that will be applied to the tidied result before outputting.
 It is expected to take the full content as a string in its input, and output
@@ -211,17 +261,17 @@ errors in the input parameters.  This can happen for example if a parameter is
 misspelled or given an invalid value.  The calling program should check for
 this flag because if it is set the destination stream will be empty or
 incomplete and should be ignored.  Error messages in the B<stderr> stream will
-indicate the cause of any problem.  
+indicate the cause of any problem.
 
 An exit value of 2 indicates that perltidy ran to completion but there there
 are warning messages in the B<stderr> stream related to parameter errors or
 conflicts and/or warning messages in the B<errorfile> stream relating to
-possible syntax errors in the source code being tidied. 
+possible syntax errors in the source code being tidied.
 
 In the event of a catastrophic error for which recovery is not possible
 B<perltidy> terminates by making calls to B<croak> or B<confess> to help the
 programmer localize the problem.  These should normally only occur during
-program development.  
+program development.
 
 =head1 NOTES ON FORMATTING PARAMETERS
 
@@ -230,15 +280,15 @@ F<.perltidyrc> configuration file, in the B<perltidyrc> parameter, and in the
 B<argv> parameter.
 
 The B<-syn> (B<--check-syntax>) flag may be used with all source and
-destination streams except for standard input and output.  However 
-data streams which are not associated with a filename will 
+destination streams except for standard input and output.  However
+data streams which are not associated with a filename will
 be copied to a temporary file before being passed to Perl.  This
 use of temporary files can cause somewhat confusing output from Perl.
 
 If the B<-pbp> style is used it will typically be necessary to also
 specify a B<-nst> flag.  This is necessary to turn off the B<-st> flag
 contained in the B<-pbp> parameter set which otherwise would direct
-the output stream to the standard output.  
+the output stream to the standard output.
 
 =head1 EXAMPLES
 
@@ -246,13 +296,13 @@ The following example uses string references to hold the input and output
 code and error streams, and illustrates checking for errors.
 
   use Perl::Tidy;
-  
+
   my $source_string = <<'EOT';
   my$error=Perl::Tidy::perltidy(argv=>$argv,source=>\$source_string,
     destination=>\$dest_string,stderr=>\$stderr_string,
   errorfile=>\$errorfile_string,);
   EOT
-  
+
   my $dest_string;
   my $stderr_string;
   my $errorfile_string;
@@ -261,9 +311,9 @@ code and error streams, and illustrates checking for errors.
   $argv .= " -nst";     # Must turn off -st in case -pbp is specified
   $argv .= " -se";      # -se appends the errorfile to stderr
   ## $argv .= " --spell-check";  # uncomment to trigger an error
-  
+
   print "<<RAW SOURCE>>\n$source_string\n";
-  
+
   my $error = Perl::Tidy::perltidy(
       argv        => $argv,
       source      => \$source_string,
@@ -272,19 +322,19 @@ code and error streams, and illustrates checking for errors.
       errorfile   => \$errorfile_string,    # ignored when -se flag is set
       ##phasers   => 'stun',                # uncomment to trigger an error
   );
-  
+
   if ($error) {
-  
+
       # serious error in input parameters, no tidied output
       print "<<STDERR>>\n$stderr_string\n";
       die "Exiting because of serious errors\n";
   }
-  
+
   if ($dest_string)      { print "<<TIDIED SOURCE>>\n$dest_string\n" }
   if ($stderr_string)    { print "<<STDERR>>\n$stderr_string\n" }
   if ($errorfile_string) { print "<<.ERR file>>\n$errorfile_string\n" }
 
-Additional examples are given in examples section of the perltidy distribution.  
+Additional examples are given in examples section of the perltidy distribution.
 
 =head1 Using the B<formatter> Callback Object
 
@@ -295,13 +345,13 @@ formatting options which are built into perltidy (beautification or
 html) are ignored.  The following diagram illustrates the logical flow:
 
                     |-- (normal route)   -> code beautification
-  caller->perltidy->|-- (-html flag )    -> create html 
+  caller->perltidy->|-- (-html flag )    -> create html
                     |-- (formatter given)-> callback to write_line
 
-This can be useful for processing perl scripts in some way.  The 
+This can be useful for processing perl scripts in some way.  The
 parameter C<$formatter> in the perltidy call,
 
-        formatter   => $formatter,  
+        formatter   => $formatter,
 
 is an object created by the caller with a C<write_line> method which
 will accept and process tokenized lines, one line per call.  Here is
@@ -309,7 +359,7 @@ a simple example of a C<write_line> which merely prints the line number,
 the line type (as determined by perltidy), and the text of the line:
 
  sub write_line {
+
      # This is called from perltidy line-by-line
      my $self              = shift;
      my $line_of_tokens    = shift;
@@ -350,11 +400,11 @@ can slow down processing.  Here is a B<write_line>, from the example
 program B<find_naughty.pl>, which does that:
 
  sub write_line {
+
      # This is called back from perltidy line-by-line
      # We're looking for $`, $&, and $'
      my ( $self, $line_of_tokens ) = @_;
+
      # pull out some stuff we might need
      my $line_type         = $line_of_tokens->{_line_type};
      my $input_line_number = $line_of_tokens->{_line_number};
@@ -362,19 +412,19 @@ program B<find_naughty.pl>, which does that:
      my $rtoken_type       = $line_of_tokens->{_rtoken_type};
      my $rtokens           = $line_of_tokens->{_rtokens};
      chomp $input_line;
+
      # skip comments, pod, etc
      return if ( $line_type ne 'CODE' );
+
      # loop over tokens looking for $`, $&, and $'
      for ( my $j = 0 ; $j < @$rtoken_type ; $j++ ) {
+
          # we only want to examine token types 'i' (identifier)
          next unless $$rtoken_type[$j] eq 'i';
+
          # pull out the actual token text
          my $token = $$rtokens[$j];
+
          # and check it
          if ( $token =~ /^\$[\`\&\']$/ ) {
              print STDERR
@@ -412,7 +462,7 @@ to get started is to find one of the examples which most closely matches
 your application and start modifying it.
 
 For help with perltidy's peculiar way of breaking lines into tokens, you
-might run, from the command line, 
+might run, from the command line,
 
  perltidy -D filename
 
@@ -432,7 +482,7 @@ The module 'Perl::Tidy' comes with a binary 'perltidy' which is installed when t
 
 =head1 VERSION
 
-This man page documents Perl::Tidy version 20210717
+This man page documents Perl::Tidy version 20220217
 
 =head1 LICENSE
 
@@ -445,7 +495,7 @@ Please refer to the file "COPYING" for details.
 
 A list of current bugs and issues can be found at the CPAN site L<https://rt.cpan.org/Public/Dist/Display.html?Name=Perl-Tidy>
 
-To report a new bug or problem, use the link on this page.  
+To report a new bug or problem, use the link on this page.
 
 The source code repository is at L<https://github.com/perltidy/perltidy>.
 
index 8eeb2d1ce8162c75b860af5e44a99666bf08975c..1161a93267554c12a18f6e9031117104e53bfac4 100644 (file)
@@ -7,7 +7,7 @@
 package Perl::Tidy::Debugger;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 sub new {
 
index 2b703ee7e2c4f15fe96751f8d38027d2b44c90e5..399d0090943c3e2ba9a6b64fb1ed1e887ea6688f 100644 (file)
@@ -7,7 +7,7 @@
 package Perl::Tidy::DevNull;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 sub new   { my $self = shift; return bless {}, $self }
 sub print { return }
 sub close { return }
index c74ab9acd5b7d5fc0eaa301e7ceae8d6b4a228a4..8bd6a2f3ecede8f8606a7fa0447abaee3078355c 100644 (file)
@@ -20,7 +20,7 @@
 package Perl::Tidy::Diagnostics;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 sub AUTOLOAD {
 
index 9a9d62f20a7517f8333c3eed95872323c1923f41..934d960981682affe31c9282a921073539ce9c62 100644 (file)
@@ -7,7 +7,7 @@
 package Perl::Tidy::FileWriter;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 use constant DEVEL_MODE => 0;
 
@@ -44,7 +44,8 @@ my $MAX_NAG_MESSAGES = 6;
 
 BEGIN {
 
-    # Array index names for variables
+    # Array index names for variables.
+    # Do not combine with other BEGIN blocks (c101).
     my $i = 0;
     use constant {
         _line_sink_object_            => $i++,
@@ -70,6 +71,39 @@ BEGIN {
     };
 }
 
+sub Die {
+    my ($msg) = @_;
+    Perl::Tidy::Die($msg);
+    return;
+}
+
+sub Fault {
+    my ($msg) = @_;
+
+    # This routine is called for errors that really should not occur
+    # except if there has been a bug introduced by a recent program change.
+    # Please add comments at calls to Fault to explain why the call
+    # should not occur, and where to look to fix it.
+    my ( $package0, $filename0, $line0, $subroutine0 ) = caller(0);
+    my ( $package1, $filename1, $line1, $subroutine1 ) = caller(1);
+    my ( $package2, $filename2, $line2, $subroutine2 ) = caller(2);
+
+    Die(<<EOM);
+==============================================================================
+While operating on input stream with name: '$input_stream_name'
+A fault was detected at line $line0 of sub '$subroutine1'
+in file '$filename1'
+which was called from line $line1 of sub '$subroutine2'
+Message: '$msg'
+This is probably an error introduced by a recent programming change.
+Perl::Tidy::FileWriter.pm reports VERSION='$VERSION'.
+==============================================================================
+EOM
+
+    # This return is to keep Perl-Critic from complaining.
+    return;
+}
+
 sub warning {
     my ( $self, $msg ) = @_;
     my $logger_object = $self->[_logger_object_];
@@ -268,15 +302,13 @@ $str
 This is probably due to a recent programming change and needs to be fixed.
 EOM
 
-                # FIXME: it would be best to set a 'severe_error' flag here and
-                # tell caller to output the original file
+                if (DEVEL_MODE) { Fault($msg) }
+
                 $self->warning($msg);
 
                 # Only issue this warning once
                 $self->[_K_sequence_error_msg_] = $msg;
 
-                # stop here in DEVEL mode so this issue doesn't get missed
-                DEVEL_MODE && Perl::Tidy::Die($msg);
             }
         }
         $self->[_K_last_arrival_] = $K;
index 6d4c2785a94e6a3feb2238749b25d6fff0739d91..b11864419eadd2d75a88a8c5bc038b38e3cd2e83 100644 (file)
 # CODE SECTION 9: Process batches of code
 #                 sub grind_batch_of_CODE
 # CODE SECTION 10: Code to break long statments
-#                  sub set_continuation_breaks
+#                  sub break_long_lines
 # CODE SECTION 11: Code to break long lists
-#                  sub scan_list
+#                  sub break_lists
 # CODE SECTION 12: Code for setting indentation
-# CODE SECTION 13: Preparing batches for vertical alignment
-#                  sub send_lines_to_vertical_aligner
+# CODE SECTION 13: Preparing batch of lines for vertical alignment
+#                  sub convey_batch_to_vertical_aligner
 # CODE SECTION 14: Code for creating closing side comments
 #                  sub add_closing_side_comment
 # CODE SECTION 15: Summarize
@@ -43,13 +43,13 @@ package Perl::Tidy::Formatter;
 use strict;
 use warnings;
 
-# this can be turned on for extra checking during development
+# This flag gets switched on during automated testing for extra checking
 use constant DEVEL_MODE => 0;
 
 { #<<< A non-indenting brace to contain all lexical variables
 
 use Carp;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 # The Tokenizer will be loaded with the Formatter
 ##use Perl::Tidy::Tokenizer;    # for is_keyword()
@@ -112,7 +112,7 @@ A fault was detected at line $line0 of sub '$subroutine1'
 in file '$filename1'
 which was called from line $line1 of sub '$subroutine2'
 Message: '$msg'
-This is probably an error introduced by a recent programming change. 
+This is probably an error introduced by a recent programming change.
 Perl::Tidy::Formatter.pm reports VERSION='$VERSION'.
 ==============================================================================
 EOM
@@ -131,69 +131,79 @@ sub Exit {
 # Global variables ...
 my (
 
-    ##################################################################
+    #-----------------------------------------------------------------
     # Section 1: Global variables which are either always constant or
     # are constant after being configured by user-supplied
     # parameters.  They remain constant as a file is being processed.
-    ##################################################################
+    #-----------------------------------------------------------------
 
     # user parameters and shortcuts
     $rOpts,
-    $rOpts_closing_side_comment_maximum_text,
-    $rOpts_continuation_indentation,
-    $rOpts_indent_columns,
-    $rOpts_line_up_parentheses,
-    $rOpts_maximum_line_length,
-    $rOpts_variable_maximum_line_length,
+    $rOpts_add_newlines,
+    $rOpts_add_whitespace,
+    $rOpts_blank_lines_after_opening_block,
     $rOpts_block_brace_tightness,
     $rOpts_block_brace_vertical_tightness,
-    $rOpts_stack_closing_block_brace,
-    $rOpts_maximum_consecutive_blank_lines,
-
-    $rOpts_recombine,
-    $rOpts_add_newlines,
+    $rOpts_break_after_labels,
+    $rOpts_break_at_old_attribute_breakpoints,
     $rOpts_break_at_old_comma_breakpoints,
-    $rOpts_ignore_old_breakpoints,
-
-    $rOpts_keep_interior_semicolons,
-    $rOpts_comma_arrow_breakpoints,
-    $rOpts_maximum_fields_per_table,
-    $rOpts_one_line_block_semicolons,
+    $rOpts_break_at_old_keyword_breakpoints,
+    $rOpts_break_at_old_logical_breakpoints,
     $rOpts_break_at_old_semicolon_breakpoints,
-
-    $rOpts_tee_side_comments,
-    $rOpts_tee_block_comments,
-    $rOpts_tee_pod,
-    $rOpts_delete_side_comments,
+    $rOpts_break_at_old_ternary_breakpoints,
+    $rOpts_break_open_paren_list,
+    $rOpts_closing_side_comments,
+    $rOpts_closing_side_comment_else_flag,
+    $rOpts_closing_side_comment_maximum_text,
+    $rOpts_comma_arrow_breakpoints,
+    $rOpts_continuation_indentation,
     $rOpts_delete_closing_side_comments,
-    $rOpts_format_skipping,
-    $rOpts_indent_only,
-    $rOpts_static_block_comments,
-
-    $rOpts_add_whitespace,
     $rOpts_delete_old_whitespace,
+    $rOpts_delete_side_comments,
+    $rOpts_extended_continuation_indentation,
+    $rOpts_format_skipping,
     $rOpts_freeze_whitespace,
     $rOpts_function_paren_vertical_alignment,
-    $rOpts_whitespace_cycle,
+    $rOpts_fuzzy_line_length,
+    $rOpts_ignore_old_breakpoints,
     $rOpts_ignore_side_comment_lengths,
-
-    $rOpts_break_at_old_attribute_breakpoints,
-    $rOpts_break_at_old_keyword_breakpoints,
-    $rOpts_break_at_old_logical_breakpoints,
-    $rOpts_break_at_old_ternary_breakpoints,
+    $rOpts_indent_closing_brace,
+    $rOpts_indent_columns,
+    $rOpts_indent_only,
+    $rOpts_keep_interior_semicolons,
+    $rOpts_line_up_parentheses,
+    $rOpts_logical_padding,
+    $rOpts_maximum_consecutive_blank_lines,
+    $rOpts_maximum_fields_per_table,
+    $rOpts_maximum_line_length,
+    $rOpts_one_line_block_semicolons,
+    $rOpts_opening_brace_always_on_right,
+    $rOpts_outdent_keywords,
+    $rOpts_outdent_labels,
+    $rOpts_outdent_long_comments,
+    $rOpts_outdent_long_quotes,
+    $rOpts_outdent_static_block_comments,
+    $rOpts_recombine,
     $rOpts_short_concatenation_item_length,
-    $rOpts_closing_side_comment_else_flag,
-    $rOpts_fuzzy_line_length,
+    $rOpts_stack_closing_block_brace,
+    $rOpts_static_block_comments,
+    $rOpts_sub_alias_list,
+    $rOpts_tee_block_comments,
+    $rOpts_tee_pod,
+    $rOpts_tee_side_comments,
+    $rOpts_variable_maximum_line_length,
+    $rOpts_valign,
+    $rOpts_valign_code,
+    $rOpts_valign_side_comments,
+    $rOpts_whitespace_cycle,
+    $rOpts_extended_line_up_parentheses,
 
     # Static hashes initialized in a BEGIN block
     %is_assignment,
-    %is_keyword_returning_list,
     %is_if_unless_and_or_last_next_redo_return,
     %is_if_elsif_else_unless_while_until_for_foreach,
-    %is_if_unless_while_until_for,
+    %is_if_unless_while_until_for_foreach,
     %is_last_next_redo_return,
-    %is_sort_map_grep,
-    %is_sort_map_grep_eval,
     %is_if_unless,
     %is_and_or,
     %is_chain_operator,
@@ -204,7 +214,6 @@ my (
     %is_opening_token,
     %is_closing_token,
     %is_equal_or_fat_comma,
-    %is_block_with_ci,
     %is_counted_type,
     %is_opening_sequence_token,
     %is_closing_sequence_token,
@@ -221,6 +230,15 @@ my (
     %is_anon_sub_1_brace_follower,
     %is_other_brace_follower,
 
+    # Initialized and re-initialized in sub initialize_grep_and_friends;
+    # These can be modified by grep-alias-list
+    %is_sort_map_grep,
+    %is_sort_map_grep_eval,
+    %is_sort_map_grep_eval_do,
+    %is_block_with_ci,
+    %is_keyword_returning_list,
+    %block_type_map,
+
     # Initialized in sub initialize_whitespace_hashes;
     # Some can be modified according to user parameters.
     %binary_ws_rules,
@@ -237,7 +255,6 @@ my (
 
     # Initialized in check_options, modified by prepare_cuddled_block_types:
     %want_one_line_block,
-    %is_braces_left_exclude_block,
 
     # Initialized in sub prepare_cuddled_block_types
     $rcuddled_block_types,
@@ -266,19 +283,22 @@ my (
     %stack_closing_token,
 
     %weld_nested_exclusion_rules,
-    %line_up_parentheses_exclusion_rules,
+    %line_up_parentheses_control_hash,
+    $line_up_parentheses_control_is_lxpl,
 
     # regex patterns for text identification.
     # Most are initialized in a sub make_**_pattern during configuration.
     # Most can be configured by user parameters.
     $SUB_PATTERN,
     $ASUB_PATTERN,
-    $ANYSUB_PATTERN,
     $static_block_comment_pattern,
     $static_side_comment_pattern,
     $format_skipping_pattern_begin,
     $format_skipping_pattern_end,
     $non_indenting_brace_pattern,
+    $bl_exclusion_pattern,
+    $bl_pattern,
+    $bli_exclusion_pattern,
     $bli_pattern,
     $block_brace_vertical_tightness_pattern,
     $blank_lines_after_opening_block_pattern,
@@ -292,13 +312,15 @@ my (
     # from level.
     @maximum_line_length_at_level,
     @maximum_text_length_at_level,
+    $stress_level_alpha,
+    $stress_level_beta,
 
     # Total number of sequence items in a weld, for quick checks
     $total_weld_count,
 
-    #########################################################
+    #--------------------------------------------------------
     # Section 2: Work arrays for the current batch of tokens.
-    #########################################################
+    #--------------------------------------------------------
 
     # These are re-initialized for each batch of code
     # in sub initialize_batch_variables.
@@ -312,6 +334,7 @@ my (
     @levels_to_go,
     @leading_spaces_to_go,
     @reduced_spaces_to_go,
+    @standard_spaces_to_go,
     @mate_index_to_go,
     @ci_levels_to_go,
     @nesting_depth_to_go,
@@ -328,18 +351,15 @@ my (
 
 BEGIN {
 
-    # Initialize constants...
-
-    # Array index names for token variables
+    # Index names for token variables.
+    # Do not combine with other BEGIN blocks (c101).
     my $i = 0;
     use constant {
-        _BLOCK_TYPE_        => $i++,
         _CI_LEVEL_          => $i++,
         _CUMULATIVE_LENGTH_ => $i++,
         _LINE_INDEX_        => $i++,
         _KNEXT_SEQ_ITEM_    => $i++,
         _LEVEL_             => $i++,
-        _SLEVEL_            => $i++,
         _TOKEN_             => $i++,
         _TOKEN_LENGTH_      => $i++,
         _TYPE_              => $i++,
@@ -348,14 +368,25 @@ BEGIN {
         # Number of token variables; must be last in list:
         _NVARS => $i++,
     };
+}
+
+BEGIN {
 
-    # Array index names for $self (which is an array ref)
-    $i = 0;
+    # Index names for $self variables.
+    # Do not combine with other BEGIN blocks (c101).
+    my $i = 0;
     use constant {
         _rlines_                    => $i++,
         _rlines_new_                => $i++,
         _rLL_                       => $i++,
         _Klimit_                    => $i++,
+        _rdepth_of_opening_seqno_   => $i++,
+        _rSS_                       => $i++,
+        _Iss_opening_               => $i++,
+        _Iss_closing_               => $i++,
+        _rblock_type_of_seqno_      => $i++,
+        _ris_asub_block_            => $i++,
+        _ris_sub_block_             => $i++,
         _K_opening_container_       => $i++,
         _K_closing_container_       => $i++,
         _K_opening_ternary_         => $i++,
@@ -374,8 +405,10 @@ BEGIN {
         _rhas_broken_code_block_    => $i++,
         _rhas_ternary_              => $i++,
         _ris_excluded_lp_container_ => $i++,
+        _rlp_object_by_seqno_       => $i++,
         _rwant_reduced_ci_          => $i++,
         _rno_xci_by_seqno_          => $i++,
+        _rbrace_left_               => $i++,
         _ris_bli_container_         => $i++,
         _rparent_of_seqno_          => $i++,
         _rchildren_of_seqno_        => $i++,
@@ -426,10 +459,13 @@ BEGIN {
 
         _rspecial_side_comment_type_ => $i++,
 
-        _rseqno_controlling_my_ci_ => $i++,
-        _ris_seqno_controlling_ci_ => $i++,
-        _save_logfile_             => $i++,
-        _maximum_level_            => $i++,
+        _rseqno_controlling_my_ci_    => $i++,
+        _ris_seqno_controlling_ci_    => $i++,
+        _save_logfile_                => $i++,
+        _maximum_level_               => $i++,
+        _maximum_level_at_line_       => $i++,
+        _maximum_BLOCK_level_         => $i++,
+        _maximum_BLOCK_level_at_line_ => $i++,
 
         _rKrange_code_without_comments_ => $i++,
         _rbreak_before_Kfirst_          => $i++,
@@ -441,30 +477,40 @@ BEGIN {
         _rending_multiline_qw_seqno_by_K_   => $i++,
         _rKrange_multiline_qw_by_seqno_     => $i++,
         _rmultiline_qw_has_extra_level_     => $i++,
-        _rbreak_before_container_by_seqno_  => $i++,
-        _ris_essential_old_breakpoint_      => $i++,
-        _roverride_cab3_                    => $i++,
-        _ris_assigned_structure_            => $i++,
+
+        _rcollapsed_length_by_seqno_       => $i++,
+        _rbreak_before_container_by_seqno_ => $i++,
+        _ris_essential_old_breakpoint_     => $i++,
+        _roverride_cab3_                   => $i++,
+        _ris_assigned_structure_           => $i++,
+
+        _LAST_SELF_INDEX_ => $i - 1,
     };
+}
+
+BEGIN {
 
-    # Array index names for _this_batch_ (in above list)
-    # So _this_batch_ is a sub-array of $self for
-    # holding the batches of tokens being processed.
-    $i = 0;
+    # Index names for batch variables.
+    # Do not combine with other BEGIN blocks (c101).
+    # These are stored in _this_batch_, which is a sub-array of $self.
+    my $i = 0;
     use constant {
-        _starting_in_quote_        => $i++,
-        _ending_in_quote_          => $i++,
-        _is_static_block_comment_  => $i++,
-        _rlines_K_                 => $i++,
-        _do_not_pad_               => $i++,
-        _ibeg0_                    => $i++,
-        _peak_batch_size_          => $i++,
-        _max_index_to_go_          => $i++,
-        _rK_to_go_                 => $i++,
-        _batch_count_              => $i++,
-        _rix_seqno_controlling_ci_ => $i++,
-        _batch_CODE_type_          => $i++,
+        _starting_in_quote_          => $i++,
+        _ending_in_quote_            => $i++,
+        _is_static_block_comment_    => $i++,
+        _ri_first_                   => $i++,
+        _ri_last_                    => $i++,
+        _do_not_pad_                 => $i++,
+        _peak_batch_size_            => $i++,
+        _max_index_to_go_            => $i++,
+        _batch_count_                => $i++,
+        _rix_seqno_controlling_ci_   => $i++,
+        _batch_CODE_type_            => $i++,
+        _ri_starting_one_line_block_ => $i++,
     };
+}
+
+BEGIN {
 
     # Sequence number assigned to the root of sequence tree.
     # The minimum of the actual sequences numbers is 4, so we can use 1
@@ -494,9 +540,10 @@ BEGIN {
     # Maximum number of little messages; probably need not be changed.
     use constant MAX_NAG_MESSAGES => 6;
 
-    # increment between sequence numbers for each type
-    # For example, ?: pairs might have numbers 7,11,15,...
-    use constant TYPE_SEQUENCE_INCREMENT => 4;
+    # This is the decimal range of printable characters in ASCII.  It is used to
+    # make quick preliminary checks before resorting to using a regex.
+    use constant ORD_PRINTABLE_MIN => 33;
+    use constant ORD_PRINTABLE_MAX => 126;
 
     # Initialize constant hashes ...
     my @q;
@@ -509,16 +556,6 @@ BEGIN {
     );
     @is_assignment{@q} = (1) x scalar(@q);
 
-    @q = qw(
-      grep
-      keys
-      map
-      reverse
-      sort
-      split
-    );
-    @is_keyword_returning_list{@q} = (1) x scalar(@q);
-
     @q = qw(is if unless and or err last next redo return);
     @is_if_unless_and_or_last_next_redo_return{@q} = (1) x scalar(@q);
 
@@ -530,18 +567,27 @@ BEGIN {
     @is_if_elsif_else_unless_while_until_for_foreach{@q} =
       (1) x scalar(@q);
 
-    @q = qw(if unless while until for);
-    @is_if_unless_while_until_for{@q} =
+    @q = qw(if unless while until for foreach);
+    @is_if_unless_while_until_for_foreach{@q} =
       (1) x scalar(@q);
 
     @q = qw(last next redo return);
     @is_last_next_redo_return{@q} = (1) x scalar(@q);
 
-    @q = qw(sort map grep);
-    @is_sort_map_grep{@q} = (1) x scalar(@q);
-
-    @q = qw(sort map grep eval);
-    @is_sort_map_grep_eval{@q} = (1) x scalar(@q);
+    # Map related block names into a common name to allow vertical alignment
+    # used by sub make_alignment_patterns. Note: this is normally unchanged,
+    # but it contains 'grep' and can be re-initized in
+    # sub initialize_grep_and_friends in a testing mode.
+    %block_type_map = (
+        'unless'  => 'if',
+        'else'    => 'if',
+        'elsif'   => 'if',
+        'when'    => 'if',
+        'default' => 'if',
+        'case'    => 'if',
+        'sort'    => 'map',
+        'grep'    => 'map',
+    );
 
     @q = qw(if unless);
     @is_if_unless{@q} = (1) x scalar(@q);
@@ -605,7 +651,7 @@ BEGIN {
     @q = qw< } ) ] : >;
     @is_closing_sequence_token{@q} = (1) x scalar(@q);
 
-    # a hash needed by sub scan_list for labeling containers
+    # a hash needed by sub break_lists for labeling containers
     @q = qw( k => && || ? : . );
     @is_container_label_type{@q} = (1) x scalar(@q);
 
@@ -619,24 +665,16 @@ BEGIN {
     push @q, ',';
     @is_counted_type{@q} = (1) x scalar(@q);
 
-    # These block types can take ci.  This is used by the -xci option.
-    # Note that the 'sub' in this list is an anonymous sub.  To be more correct
-    # we could remove sub and use ASUB pattern to also handle a
-    # prototype/signature.  But that would slow things down and would probably
-    # never be useful.
-    @q = qw( do sub eval sort map grep );
-    @is_block_with_ci{@q} = (1) x scalar(@q);
-
 }
 
-{    ## begin closure to count instanes
+{    ## begin closure to count instances
 
     # methods to count instances
     my $_count = 0;
     sub get_count        { return $_count; }
     sub _increment_count { return ++$_count }
     sub _decrement_count { return --$_count }
-} ## end closure to count instanes
+} ## end closure to count instances
 
 sub new {
 
@@ -667,18 +705,16 @@ sub new {
     # initialize closure variables...
     set_logger_object($logger_object);
     set_diagnostics_object($diagnostics_object);
-    initialize_gnu_vars();
+    initialize_lp_vars();
     initialize_csc_vars();
-    initialize_scan_list();
-    initialize_saved_opening_indentation();
+    initialize_break_lists();
     initialize_undo_ci();
     initialize_process_line_of_CODE();
     initialize_grind_batch_of_CODE();
-    initialize_adjusted_indentation();
+    initialize_final_indentation_adjustment();
     initialize_postponed_breakpoint();
     initialize_batch_variables();
     initialize_forced_breakpoint_vars();
-    initialize_gnu_batch_vars();
     initialize_write_line();
 
     my $vertical_aligner_object = Perl::Tidy::VerticalAligner->new(
@@ -712,13 +748,15 @@ sub new {
     $self->[_rlines_]     = [];    # = ref to array of lines of the file
     $self->[_rlines_new_] = [];    # = ref to array of output lines
 
-    # 'rLL' = reference to the liner array of all tokens in the file.
+    # 'rLL' = reference to the continuous liner array of all tokens in a file.
     # 'LL' stands for 'Linked List'. Using a linked list was a disaster, but
-    # 'LL' stuck because it is easy to type.
+    # 'LL' stuck because it is easy to type.  The 'rLL' array is updated
+    # by sub 'respace_tokens' during reformatting.  The indexes in 'rLL' begin
+    # with '$K' by convention.
     $self->[_rLL_]    = [];
     $self->[_Klimit_] = undef;    # = maximum K index for rLL.
 
-    # Arrays for quickly traversing the structures
+    # Indexes into the rLL list
     $self->[_K_opening_container_] = {};
     $self->[_K_closing_container_] = {};
     $self->[_K_opening_ternary_]   = {};
@@ -728,6 +766,22 @@ sub new {
     # Array of phantom semicolons, in case we ever need to undo them
     $self->[_rK_phantom_semicolons_] = undef;
 
+    # 'rSS' is the 'Signed Sequence' list, a continuous list of all sequence
+    # numbers with + or - indicating opening or closing. This list represents
+    # the entire container tree and is invariant under reformatting.  It can be
+    # used to quickly travel through the tree.  Indexes in the rSS array begin
+    # with '$I' by convention.  The 'Iss' arrays give the indexes in this list
+    # of opening and closing sequence numbers.
+    $self->[_rSS_]         = [];
+    $self->[_Iss_opening_] = [];
+    $self->[_Iss_closing_] = [];
+
+    # Arrays to help traverse the tree
+    $self->[_rdepth_of_opening_seqno_] = [];
+    $self->[_rblock_type_of_seqno_]    = {};
+    $self->[_ris_asub_block_]          = {};
+    $self->[_ris_sub_block_]           = {};
+
     # Mostly list characteristics and processing flags
     $self->[_rtype_count_by_seqno_]      = {};
     $self->[_ris_function_call_paren_]   = {};
@@ -741,8 +795,10 @@ sub new {
     $self->[_rhas_broken_code_block_]    = {};
     $self->[_rhas_ternary_]              = {};
     $self->[_ris_excluded_lp_container_] = {};
+    $self->[_rlp_object_by_seqno_]       = {};
     $self->[_rwant_reduced_ci_]          = {};
     $self->[_rno_xci_by_seqno_]          = {};
+    $self->[_rbrace_left_]               = {};
     $self->[_ris_bli_container_]         = {};
     $self->[_rparent_of_seqno_]          = {};
     $self->[_rchildren_of_seqno_]        = {};
@@ -783,6 +839,8 @@ sub new {
     $self->[_in_tabbing_disagreement_]         = 0;
     $self->[_saw_VERSION_in_this_file_]        = !$rOpts->{'pass-version-line'};
     $self->[_saw_END_or_DATA_]                 = 0;
+    $self->[_first_brace_tabbing_disagreement_] = undef;
+    $self->[_in_brace_tabbing_disagreement_]    = undef;
 
     # Hashes related to container welding...
     $self->[_radjusted_levels_] = [];
@@ -796,8 +854,11 @@ sub new {
     $self->[_rseqno_controlling_my_ci_] = {};
     $self->[_ris_seqno_controlling_ci_] = {};
 
-    $self->[_rspecial_side_comment_type_] = {};
-    $self->[_maximum_level_]              = 0;
+    $self->[_rspecial_side_comment_type_]  = {};
+    $self->[_maximum_level_]               = 0;
+    $self->[_maximum_level_at_line_]       = 0;
+    $self->[_maximum_BLOCK_level_]         = 0;
+    $self->[_maximum_BLOCK_level_at_line_] = 0;
 
     $self->[_rKrange_code_without_comments_] = [];
     $self->[_rbreak_before_Kfirst_]          = {};
@@ -811,6 +872,7 @@ sub new {
     $self->[_rKrange_multiline_qw_by_seqno_]     = {};
     $self->[_rmultiline_qw_has_extra_level_]     = {};
 
+    $self->[_rcollapsed_length_by_seqno_]       = {};
     $self->[_rbreak_before_container_by_seqno_] = {};
     $self->[_ris_essential_old_breakpoint_]     = {};
     $self->[_roverride_cab3_]                   = {};
@@ -819,6 +881,21 @@ sub new {
     # This flag will be updated later by a call to get_save_logfile()
     $self->[_save_logfile_] = defined($logger_object);
 
+    # Be sure all variables in $self have been initialized above.  To find the
+    # correspondence of index numbers and array names, copy a list to a file
+    # and use the unix 'nl' command to number lines 1..
+    if (DEVEL_MODE) {
+        my @non_existant;
+        foreach ( 0 .. _LAST_SELF_INDEX_ ) {
+            if ( !exists( $self->[$_] ) ) {
+                push @non_existant, $_;
+            }
+        }
+        if (@non_existant) {
+            Fault("These indexes in self not initialized: (@non_existant)\n");
+        }
+    }
+
     bless $self, $class;
 
     # Safety check..this is not a class yet
@@ -833,6 +910,149 @@ sub new {
 # CODE SECTION 2: Some Basic Utilities
 ######################################
 
+sub check_rLL {
+
+    # Verify that the rLL array has not been auto-vivified
+    my ( $self, $msg ) = @_;
+    my $rLL    = $self->[_rLL_];
+    my $Klimit = $self->[_Klimit_];
+    my $num    = @{$rLL};
+    if (   ( defined($Klimit) && $Klimit != $num - 1 )
+        || ( !defined($Klimit) && $num > 0 ) )
+    {
+
+        # This fault can occur if the array has been accessed for an index
+        # greater than $Klimit, which is the last token index.  Just accessing
+        # the array above index $Klimit, not setting a value, can cause @rLL to
+        # increase beyond $Klimit.  If this occurs, the problem can be located
+        # by making calls to this routine at different locations in
+        # sub 'finish_formatting'.
+        $Klimit = 'undef' if ( !defined($Klimit) );
+        $msg    = "" unless $msg;
+        Fault("$msg ERROR: rLL has num=$num but Klimit='$Klimit'\n");
+    }
+    return;
+}
+
+sub check_keys {
+    my ( $rtest, $rvalid, $msg, $exact_match ) = @_;
+
+    # Check the keys of a hash:
+    # $rtest   = ref to hash to test
+    # $rvalid  = ref to hash with valid keys
+
+    # $msg = a message to write in case of error
+    # $exact_match defines the type of check:
+    #     = false: test hash must not have unknown key
+    #     = true:  test hash must have exactly same keys as known hash
+    my @unknown_keys =
+      grep { !exists $rvalid->{$_} } keys %{$rtest};
+    my @missing_keys =
+      grep { !exists $rtest->{$_} } keys %{$rvalid};
+    my $error = @unknown_keys;
+    if ($exact_match) { $error ||= @missing_keys }
+    if ($error) {
+        local $" = ')(';
+        my @expected_keys = sort keys %{$rvalid};
+        @unknown_keys = sort @unknown_keys;
+        Fault(<<EOM);
+------------------------------------------------------------------------
+Program error detected checking hash keys
+Message is: '$msg'
+Expected keys: (@expected_keys)
+Unknown key(s): (@unknown_keys)
+Missing key(s): (@missing_keys)
+------------------------------------------------------------------------
+EOM
+    }
+    return;
+}
+
+sub check_token_array {
+    my $self = shift;
+
+    # Check for errors in the array of tokens. This is only called
+    # when the DEVEL_MODE flag is set, so this Fault will only occur
+    # during code development.
+    my $rLL = $self->[_rLL_];
+    for ( my $KK = 0 ; $KK < @{$rLL} ; $KK++ ) {
+        my $nvars = @{ $rLL->[$KK] };
+        if ( $nvars != _NVARS ) {
+            my $NVARS = _NVARS;
+            my $type  = $rLL->[$KK]->[_TYPE_];
+            $type = '*' unless defined($type);
+
+            # The number of variables per token node is _NVARS and was set when
+            # the array indexes were generated. So if the number of variables
+            # is different we have done something wrong, like not store all of
+            # them in sub 'write_line' when they were received from the
+            # tokenizer.
+            Fault(
+"number of vars for node $KK, type '$type', is $nvars but should be $NVARS"
+            );
+        }
+        foreach my $var ( _TOKEN_, _TYPE_ ) {
+            if ( !defined( $rLL->[$KK]->[$var] ) ) {
+                my $iline = $rLL->[$KK]->[_LINE_INDEX_];
+
+                # This is a simple check that each token has some basic
+                # variables.  In other words, that there are no holes in the
+                # array of tokens.  Sub 'write_line' pushes tokens into the
+                # $rLL array, so this should guarantee no gaps.
+                Fault("Undefined variable $var for K=$KK, line=$iline\n");
+            }
+        }
+    }
+    return;
+}
+
+{    ## begin closure check_line_hashes
+
+    # This code checks that no autovivification occurs in the 'line' hash
+
+    my %valid_line_hash;
+
+    BEGIN {
+
+        # These keys are defined for each line in the formatter
+        # Each line must have exactly these quantities
+        my @valid_line_keys = qw(
+          _curly_brace_depth
+          _ending_in_quote
+          _guessed_indentation_level
+          _line_number
+          _line_text
+          _line_type
+          _paren_depth
+          _quote_character
+          _rK_range
+          _square_bracket_depth
+          _starting_in_quote
+          _ended_in_blank_token
+          _code_type
+
+          _ci_level_0
+          _level_0
+          _nesting_blocks_0
+          _nesting_tokens_0
+        );
+
+        @valid_line_hash{@valid_line_keys} = (1) x scalar(@valid_line_keys);
+    }
+
+    sub check_line_hashes {
+        my $self   = shift;
+        my $rlines = $self->[_rlines_];
+        foreach my $rline ( @{$rlines} ) {
+            my $iline     = $rline->{_line_number};
+            my $line_type = $rline->{_line_type};
+            check_keys( $rline, \%valid_line_hash,
+                "Checkpoint: line number =$iline,  line_type=$line_type", 1 );
+        }
+        return;
+    }
+} ## end closure check_line_hashes
+
 {    ## begin closure for logger routines
     my $logger_object;
 
@@ -877,13 +1097,6 @@ sub new {
         return;
     }
 
-    sub report_definite_bug {
-        if ($logger_object) {
-            $logger_object->report_definite_bug();
-        }
-        return;
-    }
-
     sub get_saw_brace_error {
         if ($logger_object) {
             return $logger_object->get_saw_brace_error();
@@ -934,44 +1147,6 @@ sub get_output_line_number {
     return $vao->get_output_line_number();
 }
 
-sub check_token_array {
-    my $self = shift;
-
-    # Check for errors in the array of tokens. This is only called now
-    # when the DEVEL_MODE flag is set, so this Fault will only occur
-    # during code development.
-    my $rLL = $self->[_rLL_];
-    for ( my $KK = 0 ; $KK < @{$rLL} ; $KK++ ) {
-        my $nvars = @{ $rLL->[$KK] };
-        if ( $nvars != _NVARS ) {
-            my $NVARS = _NVARS;
-            my $type  = $rLL->[$KK]->[_TYPE_];
-            $type = '*' unless defined($type);
-
-            # The number of variables per token node is _NVARS and was set when
-            # the array indexes were generated. So if the number of variables
-            # is different we have done something wrong, like not store all of
-            # them in sub 'write_line' when they were received from the
-            # tokenizer.
-            Fault(
-"number of vars for node $KK, type '$type', is $nvars but should be $NVARS"
-            );
-        }
-        foreach my $var ( _TOKEN_, _TYPE_ ) {
-            if ( !defined( $rLL->[$KK]->[$var] ) ) {
-                my $iline = $rLL->[$KK]->[_LINE_INDEX_];
-
-                # This is a simple check that each token has some basic
-                # variables.  In other words, that there are no holes in the
-                # array of tokens.  Sub 'write_line' pushes tokens into the
-                # $rLL array, so this should guarantee no gaps.
-                Fault("Undefined variable $var for K=$KK, line=$iline\n");
-            }
-        }
-    }
-    return;
-}
-
 sub want_blank_line {
     my $self = shift;
     $self->flush();
@@ -996,15 +1171,6 @@ sub consecutive_nonblank_lines {
       $vao->get_cached_line_count();
 }
 
-sub trim {
-
-    # trim leading and trailing whitespace from a string
-    my $str = shift;
-    $str =~ s/\s+$//;
-    $str =~ s/^\s+//;
-    return $str;
-}
-
 sub max {
     my (@vals) = @_;
     my $max = shift @vals;
@@ -1043,6 +1209,9 @@ sub check_options {
     initialize_whitespace_hashes();
     initialize_bond_strength_hashes();
 
+    # This function must be called early to get hashes with grep initialized
+    initialize_grep_and_friends( $rOpts->{'grep-alias-list'} );
+
     # Make needed regex patterns for matching text.
     # NOTE: sub_matching_patterns must be made first because later patterns use
     # them; see RT #133130.
@@ -1086,24 +1255,28 @@ sub check_options {
     }
 
     make_bli_pattern();
+    make_bl_pattern();
     make_block_brace_vertical_tightness_pattern();
     make_blank_line_pattern();
     make_keyword_group_list_pattern();
 
     # Make initial list of desired one line block types
     # They will be modified by 'prepare_cuddled_block_types'
+    # NOTE: this line must come after is_sort_map_grep_eval is
+    # initialized in sub 'initialize_grep_and_friends'
     %want_one_line_block = %is_sort_map_grep_eval;
 
-    # Default is to exclude one-line block types from -bl formatting
-    # FIXME: Eventually a flag should be added to modify this.
-    %is_braces_left_exclude_block = %is_sort_map_grep_eval;
-
     prepare_cuddled_block_types();
     if ( $rOpts->{'dump-cuddled-block-list'} ) {
         dump_cuddled_block_list(*STDOUT);
         Exit(0);
     }
 
+    # -xlp implies -lp
+    if ( $rOpts->{'extended-line-up-parentheses'} ) {
+        $rOpts->{'line-up-parentheses'} ||= 1;
+    }
+
     if ( $rOpts->{'line-up-parentheses'} ) {
 
         if (   $rOpts->{'indent-only'}
@@ -1119,7 +1292,8 @@ arbitrarily large numbers of line breakpoints.  This isn't possible
 with these flags.
 -----------------------------------------------------------------------
 EOM
-            $rOpts->{'line-up-parentheses'} = 0;
+            $rOpts->{'line-up-parentheses'}          = 0;
+            $rOpts->{'extended-line-up-parentheses'} = 0;
         }
 
         if ( $rOpts->{'whitespace-cycle'} ) {
@@ -1264,6 +1438,7 @@ EOM
                   ( $lbs, $rbs );
             }
         }
+        return;
     };
 
     my $break_before = sub {
@@ -1276,6 +1451,7 @@ EOM
                   ( $lbs, $rbs );
             }
         }
+        return;
     };
 
     $break_after->(@all_operators) if ( $rOpts->{'break-after-all-operators'} );
@@ -1312,6 +1488,57 @@ EOM
         $break_before_container_types{'('} = $_ if $_ && $_ > 0;
     }
 
+    #--------------------------------------------------------------
+    # The combination -lp -iob -vmll -bbx=2 can be unstable (b1266)
+    #--------------------------------------------------------------
+    # The -vmll and -lp parameters do not really work well together.
+    # To avoid instabilities, we will change any -bbx=2 to -bbx=1 (stable).
+    # NOTE: we could make this more precise by looking at any exclusion
+    # flags for -lp, and allowing -bbx=2 for excluded types.
+    if (   $rOpts->{'variable-maximum-line-length'}
+        && $rOpts->{'ignore-old-breakpoints'}
+        && $rOpts->{'line-up-parentheses'} )
+    {
+        my @changed;
+        foreach my $key ( keys %break_before_container_types ) {
+            if ( $break_before_container_types{$key} == 2 ) {
+                $break_before_container_types{$key} = 1;
+                push @changed, $key;
+            }
+        }
+        if (@changed) {
+
+            # we could write a warning here
+        }
+    }
+
+    #-------------------------------------------------------------------
+    # The combination -xlp and -vmll can be unstable unless -iscl is set
+    #-------------------------------------------------------------------
+    # This is a temporary fix for issue b1302.  See also b1306, b1310.
+    # FIXME: look for a better fix.
+    if (   $rOpts->{'variable-maximum-line-length'}
+        && $rOpts->{'extended-line-up-parentheses'}
+        && !$rOpts->{'ignore-side-comment-lengths'} )
+    {
+        $rOpts->{'ignore-side-comment-lengths'} = 1;
+
+        # we could write a warning here
+    }
+
+    #-----------------------------------------------------------
+    # The combination -lp -vmll can be unstable if -ci<2 (b1267)
+    #-----------------------------------------------------------
+    # The -vmll and -lp parameters do not really work well together.
+    # This is a very crude fix for an unusual parameter combination.
+    if (   $rOpts->{'variable-maximum-line-length'}
+        && $rOpts->{'line-up-parentheses'}
+        && $rOpts->{'continuation-indentation'} < 2 )
+    {
+        $rOpts->{'continuation-indentation'} = 2;
+        ##Warn("Increased -ci=n to n=2 for stability with -lp and -vmll\n");
+    }
+
     %container_indentation_options = ();
     foreach my $pair (
         [ 'break-before-hash-brace-and-indent',     '{' ],
@@ -1443,44 +1670,36 @@ EOM
         '?' => ':',
     );
 
-    # note any requested old line breaks to keep
-    %keep_break_before_type = ();
-    %keep_break_after_type  = ();
-    if ( !$rOpts->{'ignore-old-breakpoints'} ) {
+    if ( $rOpts->{'ignore-old-breakpoints'} ) {
 
-        # FIXME: could check for valid types here.
-        # Invalid types are harmless but probably not intended.
-        my @types;
-        @types = ( split_words( $rOpts->{'keep-old-breakpoints-before'} ) );
-        @keep_break_before_type{@types} = (1) x scalar(@types);
-        @types = ( split_words( $rOpts->{'keep-old-breakpoints-after'} ) );
-        @keep_break_after_type{@types} = (1) x scalar(@types);
-    }
-    else {
+        my @conflicts;
         if ( $rOpts->{'break-at-old-method-breakpoints'} ) {
-            Warn("Conflicting parameters: -iob and -bom; -bom will be ignored\n"
-            );
             $rOpts->{'break-at-old-method-breakpoints'} = 0;
+            push @conflicts, '--break-at-old-method-breakpoints (-bom)';
         }
         if ( $rOpts->{'break-at-old-comma-breakpoints'} ) {
-            Warn("Conflicting parameters: -iob and -boc; -boc will be ignored\n"
-            );
             $rOpts->{'break-at-old-comma-breakpoints'} = 0;
+            push @conflicts, '--break-at-old-comma-breakpoints (-boc)';
         }
         if ( $rOpts->{'break-at-old-semicolon-breakpoints'} ) {
-            Warn("Conflicting parameters: -iob and -bos; -bos will be ignored\n"
-            );
             $rOpts->{'break-at-old-semicolon-breakpoints'} = 0;
+            push @conflicts, '--break-at-old-semicolon-breakpoints (-bos)';
         }
         if ( $rOpts->{'keep-old-breakpoints-before'} ) {
-            Warn("Conflicting parameters: -iob and -kbb; -kbb will be ignored\n"
-            );
             $rOpts->{'keep-old-breakpoints-before'} = "";
+            push @conflicts, '--keep-old-breakpoints-before (-kbb)';
         }
         if ( $rOpts->{'keep-old-breakpoints-after'} ) {
-            Warn("Conflicting parameters: -iob and -kba; -kba will be ignored\n"
-            );
             $rOpts->{'keep-old-breakpoints-after'} = "";
+            push @conflicts, '--keep-old-breakpoints-after (-kba)';
+        }
+
+        if (@conflicts) {
+            my $msg = join( "\n  ",
+" Conflict: These conflicts with --ignore-old-breakponts (-iob) will be turned off:",
+                @conflicts )
+              . "\n";
+            Warn($msg);
         }
 
         # Note: These additional parameters are made inactive by -iob.
@@ -1492,68 +1711,95 @@ EOM
         $rOpts->{'break-at-old-attribute-breakpoints'} = 0;
     }
 
-    #############################################################
+    %keep_break_before_type = ();
+    initialize_keep_old_breakpoints( $rOpts->{'keep-old-breakpoints-before'},
+        'kbb', \%keep_break_before_type );
+
+    %keep_break_after_type = ();
+    initialize_keep_old_breakpoints( $rOpts->{'keep-old-breakpoints-after'},
+        'kba', \%keep_break_after_type );
+
+    #------------------------------------------------------------
     # Make global vars for frequently used options for efficiency
-    #############################################################
+    #------------------------------------------------------------
 
-    $rOpts_closing_side_comment_maximum_text =
-      $rOpts->{'closing-side-comment-maximum-text'};
-    $rOpts_continuation_indentation = $rOpts->{'continuation-indentation'};
-    $rOpts_indent_columns           = $rOpts->{'indent-columns'};
-    $rOpts_line_up_parentheses      = $rOpts->{'line-up-parentheses'};
-    $rOpts_maximum_line_length      = $rOpts->{'maximum-line-length'};
-    $rOpts_variable_maximum_line_length =
-      $rOpts->{'variable-maximum-line-length'};
+    $rOpts_add_newlines   = $rOpts->{'add-newlines'};
+    $rOpts_add_whitespace = $rOpts->{'add-whitespace'};
+    $rOpts_blank_lines_after_opening_block =
+      $rOpts->{'blank-lines-after-opening-block'};
     $rOpts_block_brace_tightness = $rOpts->{'block-brace-tightness'};
     $rOpts_block_brace_vertical_tightness =
       $rOpts->{'block-brace-vertical-tightness'};
-    $rOpts_stack_closing_block_brace = $rOpts->{'stack-closing-block-brace'};
-    $rOpts_maximum_consecutive_blank_lines =
-      $rOpts->{'maximum-consecutive-blank-lines'};
-    $rOpts_recombine    = $rOpts->{'recombine'};
-    $rOpts_add_newlines = $rOpts->{'add-newlines'};
+    $rOpts_break_after_labels = $rOpts->{'break-after-labels'};
+    $rOpts_break_at_old_attribute_breakpoints =
+      $rOpts->{'break-at-old-attribute-breakpoints'};
     $rOpts_break_at_old_comma_breakpoints =
       $rOpts->{'break-at-old-comma-breakpoints'};
-    $rOpts_ignore_old_breakpoints    = $rOpts->{'ignore-old-breakpoints'};
-    $rOpts_keep_interior_semicolons  = $rOpts->{'keep-interior-semicolons'};
-    $rOpts_comma_arrow_breakpoints   = $rOpts->{'comma-arrow-breakpoints'};
-    $rOpts_maximum_fields_per_table  = $rOpts->{'maximum-fields-per-table'};
-    $rOpts_one_line_block_semicolons = $rOpts->{'one-line-block-semicolons'};
+    $rOpts_break_at_old_keyword_breakpoints =
+      $rOpts->{'break-at-old-keyword-breakpoints'};
+    $rOpts_break_at_old_logical_breakpoints =
+      $rOpts->{'break-at-old-logical-breakpoints'};
     $rOpts_break_at_old_semicolon_breakpoints =
       $rOpts->{'break-at-old-semicolon-breakpoints'};
-
-    $rOpts_tee_side_comments    = $rOpts->{'tee-side-comments'};
-    $rOpts_tee_block_comments   = $rOpts->{'tee-block-comments'};
-    $rOpts_tee_pod              = $rOpts->{'tee-pod'};
-    $rOpts_delete_side_comments = $rOpts->{'delete-side-comments'};
+    $rOpts_break_at_old_ternary_breakpoints =
+      $rOpts->{'break-at-old-ternary-breakpoints'};
+    $rOpts_break_open_paren_list = $rOpts->{'break-open-paren-list'};
+    $rOpts_closing_side_comments = $rOpts->{'closing-side-comments'};
+    $rOpts_closing_side_comment_else_flag =
+      $rOpts->{'closing-side-comment-else-flag'};
+    $rOpts_closing_side_comment_maximum_text =
+      $rOpts->{'closing-side-comment-maximum-text'};
+    $rOpts_comma_arrow_breakpoints  = $rOpts->{'comma-arrow-breakpoints'};
+    $rOpts_continuation_indentation = $rOpts->{'continuation-indentation'};
     $rOpts_delete_closing_side_comments =
       $rOpts->{'delete-closing-side-comments'};
-    $rOpts_format_skipping       = $rOpts->{'format-skipping'};
-    $rOpts_indent_only           = $rOpts->{'indent-only'};
-    $rOpts_static_block_comments = $rOpts->{'static-block-comments'};
-
-    $rOpts_add_whitespace        = $rOpts->{'add-whitespace'};
     $rOpts_delete_old_whitespace = $rOpts->{'delete-old-whitespace'};
-    $rOpts_freeze_whitespace     = $rOpts->{'freeze-whitespace'};
-
+    $rOpts_extended_continuation_indentation =
+      $rOpts->{'extended-continuation-indentation'};
+    $rOpts_delete_side_comments = $rOpts->{'delete-side-comments'};
+    $rOpts_format_skipping      = $rOpts->{'format-skipping'};
+    $rOpts_freeze_whitespace    = $rOpts->{'freeze-whitespace'};
     $rOpts_function_paren_vertical_alignment =
       $rOpts->{'function-paren-vertical-alignment'};
+    $rOpts_fuzzy_line_length      = $rOpts->{'fuzzy-line-length'};
+    $rOpts_ignore_old_breakpoints = $rOpts->{'ignore-old-breakpoints'};
     $rOpts_ignore_side_comment_lengths =
       $rOpts->{'ignore-side-comment-lengths'};
-
-    $rOpts_break_at_old_attribute_breakpoints =
-      $rOpts->{'break-at-old-attribute-breakpoints'};
-    $rOpts_break_at_old_keyword_breakpoints =
-      $rOpts->{'break-at-old-keyword-breakpoints'};
-    $rOpts_break_at_old_logical_breakpoints =
-      $rOpts->{'break-at-old-logical-breakpoints'};
-    $rOpts_break_at_old_ternary_breakpoints =
-      $rOpts->{'break-at-old-ternary-breakpoints'};
+    $rOpts_indent_closing_brace     = $rOpts->{'indent-closing-brace'};
+    $rOpts_indent_columns           = $rOpts->{'indent-columns'};
+    $rOpts_indent_only              = $rOpts->{'indent-only'};
+    $rOpts_keep_interior_semicolons = $rOpts->{'keep-interior-semicolons'};
+    $rOpts_line_up_parentheses      = $rOpts->{'line-up-parentheses'};
+    $rOpts_extended_line_up_parentheses =
+      $rOpts->{'extended-line-up-parentheses'};
+    $rOpts_logical_padding = $rOpts->{'logical-padding'};
+    $rOpts_maximum_consecutive_blank_lines =
+      $rOpts->{'maximum-consecutive-blank-lines'};
+    $rOpts_maximum_fields_per_table  = $rOpts->{'maximum-fields-per-table'};
+    $rOpts_maximum_line_length       = $rOpts->{'maximum-line-length'};
+    $rOpts_one_line_block_semicolons = $rOpts->{'one-line-block-semicolons'};
+    $rOpts_opening_brace_always_on_right =
+      $rOpts->{'opening-brace-always-on-right'};
+    $rOpts_outdent_keywords      = $rOpts->{'outdent-keywords'};
+    $rOpts_outdent_labels        = $rOpts->{'outdent-labels'};
+    $rOpts_outdent_long_comments = $rOpts->{'outdent-long-comments'};
+    $rOpts_outdent_long_quotes   = $rOpts->{'outdent-long-quotes'};
+    $rOpts_outdent_static_block_comments =
+      $rOpts->{'outdent-static-block-comments'};
+    $rOpts_recombine = $rOpts->{'recombine'};
     $rOpts_short_concatenation_item_length =
       $rOpts->{'short-concatenation-item-length'};
-    $rOpts_closing_side_comment_else_flag =
-      $rOpts->{'closing-side-comment-else-flag'};
-    $rOpts_fuzzy_line_length = $rOpts->{'fuzzy-line-length'};
+    $rOpts_stack_closing_block_brace = $rOpts->{'stack-closing-block-brace'};
+    $rOpts_static_block_comments     = $rOpts->{'static-block-comments'};
+    $rOpts_sub_alias_list            = $rOpts->{'sub-alias-list'};
+    $rOpts_tee_block_comments        = $rOpts->{'tee-block-comments'};
+    $rOpts_tee_pod                   = $rOpts->{'tee-pod'};
+    $rOpts_tee_side_comments         = $rOpts->{'tee-side-comments'};
+    $rOpts_valign                    = $rOpts->{'valign'};
+    $rOpts_valign_code               = $rOpts->{'valign-code'};
+    $rOpts_valign_side_comments      = $rOpts->{'valign-side-comments'};
+    $rOpts_variable_maximum_line_length =
+      $rOpts->{'variable-maximum-line-length'};
 
     # Note that both opening and closing tokens can access the opening
     # and closing flags of their container types.
@@ -1657,8 +1903,141 @@ EOM
         }
     }
 
+    # Define two measures of indentation level, alpha and beta, at which some
+    # formatting features come under stress and need to start shutting down.
+    # Some combination of the two will be used to shut down different
+    # formatting features.
+    # Put a reasonable upper limit on stress level (say 100) in case the
+    # whitespace-cycle variable is used.
+    my $stress_level_limit = min( 100, $level_max );
+
+    # Find stress_level_alpha, targeted at very short maximum line lengths.
+    $stress_level_alpha = $stress_level_limit + 1;
+    foreach my $level_test ( 0 .. $stress_level_limit ) {
+        my $max_len = $maximum_text_length_at_level[ $level_test + 1 ];
+        my $excess_inside_space =
+          $max_len -
+          $rOpts_continuation_indentation -
+          $rOpts_indent_columns - 8;
+        if ( $excess_inside_space <= 0 ) {
+            $stress_level_alpha = $level_test;
+            last;
+        }
+    }
+
+    # Find stress level beta, a stress level targeted at formatting
+    # at deep levels near the maximum line length.  We start increasing
+    # from zero and stop at the first level which shows no more space.
+
+    # 'const' is a fixed number of spaces for a typical variable.
+    # Cases b1197-b1204 work ok with const=12 but not with const=8
+    my $const = 16;
+    my $denom = max( 1, $rOpts_indent_columns );
+    $stress_level_beta = 0;
+    foreach my $level ( 0 .. $stress_level_limit ) {
+        my $remaining_cycles = max(
+            0,
+            (
+                $maximum_text_length_at_level[$level] -
+                  $rOpts_continuation_indentation - $const
+            ) / $denom
+        );
+        last if ( $remaining_cycles <= 3 );    # 2 does not work
+        $stress_level_beta = $level;
+    }
+
     initialize_weld_nested_exclusion_rules($rOpts);
-    initialize_line_up_parentheses_exclusion_rules($rOpts);
+
+    %line_up_parentheses_control_hash    = ();
+    $line_up_parentheses_control_is_lxpl = 1;
+    my $lpxl = $rOpts->{'line-up-parentheses-exclusion-list'};
+    my $lpil = $rOpts->{'line-up-parentheses-inclusion-list'};
+    if ( $lpxl && $lpil ) {
+        Warn( <<EOM );
+You entered values for both -lpxl=s and -lpil=s; the -lpil list will be ignored
+EOM
+    }
+    if ($lpxl) {
+        $line_up_parentheses_control_is_lxpl = 1;
+        initialize_line_up_parentheses_control_hash(
+            $rOpts->{'line-up-parentheses-exclusion-list'}, 'lpxl' );
+    }
+    elsif ($lpil) {
+        $line_up_parentheses_control_is_lxpl = 0;
+        initialize_line_up_parentheses_control_hash(
+            $rOpts->{'line-up-parentheses-inclusion-list'}, 'lpil' );
+    }
+
+    return;
+}
+
+use constant ALIGN_GREP_ALIASES => 0;
+
+sub initialize_grep_and_friends {
+    my ($str) = @_;
+
+    # Initialize or re-initialize hashes with 'grep' and grep aliases. This
+    # must be done after each set of options because new grep aliases may be
+    # used.
+
+    # re-initialize the hash ... this is critical!
+    %is_sort_map_grep = ();
+
+    my @q = qw(sort map grep);
+    @is_sort_map_grep{@q} = (1) x scalar(@q);
+
+    # Note that any 'grep-alias-list' string has been preprocessed to be a
+    # trimmed, space-separated list.
+    my @grep_aliases = split /\s+/, $str;
+    @{is_sort_map_grep}{@grep_aliases} = (1) x scalar(@grep_aliases);
+
+    ##@q = qw(sort map grep eval);
+    %is_sort_map_grep_eval = %is_sort_map_grep;
+    $is_sort_map_grep_eval{'eval'} = 1;
+
+    ##@q = qw(sort map grep eval do);
+    %is_sort_map_grep_eval_do = %is_sort_map_grep_eval;
+    $is_sort_map_grep_eval_do{'do'} = 1;
+
+    # These block types can take ci.  This is used by the -xci option.
+    # Note that the 'sub' in this list is an anonymous sub.  To be more correct
+    # we could remove sub and use ASUB pattern to also handle a
+    # prototype/signature.  But that would slow things down and would probably
+    # never be useful.
+    ##@q = qw( do sub eval sort map grep );
+    %is_block_with_ci = %is_sort_map_grep_eval_do;
+    $is_block_with_ci{'sub'} = 1;
+
+    %is_keyword_returning_list = ();
+    @q                         = qw(
+      grep
+      keys
+      map
+      reverse
+      sort
+      split
+    );
+    push @q, @grep_aliases;
+    @is_keyword_returning_list{@q} = (1) x scalar(@q);
+
+    # This code enables vertical alignment of grep aliases for testing.  It has
+    # not been found to be beneficial, so it is off by default.  But it is
+    # useful for precise testing of the grep alias coding.
+    if (ALIGN_GREP_ALIASES) {
+        %block_type_map = (
+            'unless'  => 'if',
+            'else'    => 'if',
+            'elsif'   => 'if',
+            'when'    => 'if',
+            'default' => 'if',
+            'case'    => 'if',
+            'sort'    => 'map',
+            'grep'    => 'map',
+        );
+        foreach (@q) {
+            $block_type_map{$_} = 'map' unless ( $_ eq 'map' );
+        }
+    }
     return;
 }
 
@@ -1782,11 +2161,8 @@ EOM
     return;
 }
 
-sub initialize_line_up_parentheses_exclusion_rules {
-    my ($rOpts) = @_;
-    %line_up_parentheses_exclusion_rules = ();
-    my $opt_name = 'line-up-parentheses-exclusion-list';
-    my $str      = $rOpts->{$opt_name};
+sub initialize_line_up_parentheses_control_hash {
+    my ( $str, $opt_name ) = @_;
     return unless ($str);
     $str =~ s/^\s+//;
     $str =~ s/\s+$//;
@@ -1841,13 +2217,13 @@ sub initialize_line_up_parentheses_exclusion_rules {
             next;
         }
 
-        if ( !defined( $line_up_parentheses_exclusion_rules{$key} ) ) {
-            $line_up_parentheses_exclusion_rules{$key} = [ $flag1, $flag2 ];
+        if ( !defined( $line_up_parentheses_control_hash{$key} ) ) {
+            $line_up_parentheses_control_hash{$key} = [ $flag1, $flag2 ];
             next;
         }
 
         # check for multiple conflicting specifications
-        my $rflags = $line_up_parentheses_exclusion_rules{$key};
+        my $rflags = $line_up_parentheses_control_hash{$key};
         my $err;
         if ( defined( $rflags->[0] ) && $rflags->[0] ne $flag1 ) {
             $err = 1;
@@ -1874,27 +2250,141 @@ Only the last will be used.
 EOM
     }
 
-    # Possible speedup: we could turn off -lp if it is not actually used
-    my $all_off = 1;
-    foreach my $key (qw# ( { [ #) {
-        my $rflags = $line_up_parentheses_exclusion_rules{$key};
-        if ( defined($rflags) ) {
-            my ( $flag1, $flag2 ) = @{$rflags};
-            if ( $flag1 && $flag1 ne '*' ) { $all_off = 0; last }
-            if ($flag2)                    { $all_off = 0; last }
+    # Speedup: we can turn off -lp if it is not actually used
+    if ($line_up_parentheses_control_is_lxpl) {
+        my $all_off = 1;
+        foreach my $key (qw# ( { [ #) {
+            my $rflags = $line_up_parentheses_control_hash{$key};
+            if ( defined($rflags) ) {
+                my ( $flag1, $flag2 ) = @{$rflags};
+                if ( $flag1 && $flag1 ne '*' ) { $all_off = 0; last }
+                if ($flag2)                    { $all_off = 0; last }
+            }
+        }
+        if ($all_off) {
+            $rOpts->{'line-up-parentheses'} = "";
+        }
+    }
+
+    return;
+}
+
+use constant DEBUG_KB => 0;
+
+sub initialize_keep_old_breakpoints {
+    my ( $str, $short_name, $rkeep_break_hash ) = @_;
+    return unless $str;
+
+    my %flags = ();
+    my @list  = split_words($str);
+    if ( DEBUG_KB && @list ) {
+        local $" = ' ';
+        print <<EOM;
+DEBUG_KB entering for '$short_name' with str=$str\n";
+list is: @list;
+EOM
+    }
+
+    # - pull out any any leading container code, like f( or *{
+    foreach (@list) {
+        if ( $_ =~ /^( [ \w\* ] )( [ \{\(\[\}\)\] ] )$/x ) {
+            $_ = $2;
+            $flags{$2} = $1;
+        }
+    }
+
+    my @unknown_types;
+    foreach my $type (@list) {
+        if ( !Perl::Tidy::Tokenizer::is_valid_token_type($type) ) {
+            push @unknown_types, $type;
+        }
+    }
+
+    if (@unknown_types) {
+        my $num = @unknown_types;
+        local $" = ' ';
+        Warn(<<EOM);
+$num unrecognized token types were input with --$short_name :
+@unknown_types
+EOM
+    }
+
+    @{$rkeep_break_hash}{@list} = (1) x scalar(@list);
+
+    foreach my $key ( keys %flags ) {
+        my $flag = $flags{$key};
+
+        if ( length($flag) != 1 ) {
+            Warn(<<EOM);
+Multiple entries given for '$key' in '$short_name'
+EOM
+        }
+        elsif ( ( $key eq '(' || $key eq ')' ) && $flag !~ /^[kKfFwW\*]$/ ) {
+            Warn(<<EOM);
+Unknown flag '$flag' given for '$key' in '$short_name'
+EOM
+        }
+        elsif ( ( $key eq '}' || $key eq '}' ) && $flag !~ /^[bB\*]$/ ) {
+            Warn(<<EOM);
+Unknown flag '$flag' given for '$key' in '$short_name'
+EOM
         }
+
+        $rkeep_break_hash->{$key} = $flag;
+    }
+
+    # Temporary patch and warning during changeover from using type to token for
+    # containers .  This can be eliminated after one or two future releases.
+    if (   $rkeep_break_hash->{'{'}
+        && $rkeep_break_hash->{'{'} eq '1'
+        && !$rkeep_break_hash->{'('}
+        && !$rkeep_break_hash->{'['} )
+    {
+        $rkeep_break_hash->{'('} = 1;
+        $rkeep_break_hash->{'['} = 1;
+        Warn(<<EOM);
+Sorry, but the format for the -kbb and -kba flags is changing a little.
+You entered '{' which currently matches '{' '(' and '[',
+but in the future it will only match '{'.
+To prevent this message please do one of the following:
+  use '{ ( [' if you want to match all opening containers, or
+  use '(' or '[' to match just those containers, or
+  use '*{' to match only opening braces
+EOM
+    }
+
+    if (   $rkeep_break_hash->{'}'}
+        && $rkeep_break_hash->{'}'} eq '1'
+        && !$rkeep_break_hash->{')'}
+        && !$rkeep_break_hash->{']'} )
+    {
+        $rkeep_break_hash->{'('} = 1;
+        $rkeep_break_hash->{'['} = 1;
+        Warn(<<EOM);
+Sorry, but the format for the -kbb and -kba flags is changing a little.
+You entered '}' which currently matches each of '}' ')' and ']',
+but in the future it will only match '}'.
+To prevent this message please do one of the following:
+  use '} ) ]' if you want to match all closing containers, or
+  use ')' or ']' to match just those containers, or
+  use '*}' to match only closing braces
+EOM
     }
-    if ($all_off) {
 
-        # FIXME: This speedup works but is currently deactivated because at
-        # present users of -lp could see some discontinuities in formatting,
-        # such as those involving the choice of breaks at '='.  Only if/when
-        # these issues have been checked and resolved it should be reactivated
-        # as a speedup.
-        ## $rOpts->{'line-up-parentheses'} = "";
+    if ( DEBUG_KB && @list ) {
+        my @tmp = %flags;
+        local $" = ' ';
+        print <<EOM;
+
+DEBUG_KB -$short_name flag: $str
+final keys:  @list
+special flags:  @tmp
+EOM
+
     }
 
     return;
+
 }
 
 sub initialize_whitespace_hashes {
@@ -2027,6 +2517,18 @@ sub initialize_whitespace_hashes {
 
 } ## end initialize_whitespace_hashes
 
+# The following hash is used to skip over needless if tests.
+# Be sure to update it when adding new checks in its block.
+my %is_special_ws_type;
+
+BEGIN {
+    my @q = qw(k w i C m - Q);
+    push @q, '#';
+    @is_special_ws_type{@q} = (1) x scalar(@q);
+}
+
+use constant DEBUG_WHITE => 0;
+
 sub set_whitespace_flags {
 
     # This routine is called once per file to set whitespace flags for that
@@ -2042,8 +2544,10 @@ sub set_whitespace_flags {
     #
 
     my $self = shift;
-    my $rLL  = $self->[_rLL_];
-    use constant DEBUG_WHITE => 0;
+
+    my $rLL                  = $self->[_rLL_];
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
+    my $jmax                 = @{$rLL} - 1;
 
     my $rOpts_space_keyword_paren   = $rOpts->{'space-keyword-paren'};
     my $rOpts_space_backslash_quote = $rOpts->{'space-backslash-quote'};
@@ -2052,28 +2556,23 @@ sub set_whitespace_flags {
     my $rwhitespace_flags       = [];
     my $ris_function_call_paren = {};
 
+    return $rwhitespace_flags if ( $jmax < 0 );
+
     my %is_for_foreach = ( 'for' => 1, 'foreach' => 1 );
 
-    my ( $token, $type, $block_type, $seqno, $input_line_no );
-    my (
-        $last_token, $last_type, $last_block_type,
-        $last_seqno, $last_input_line_no
-    );
+    my ( $rtokh,      $token,      $type );
+    my ( $rtokh_last, $last_token, $last_type );
 
     my $j_tight_closing_paren = -1;
 
-    $token              = ' ';
-    $type               = 'b';
-    $block_type         = '';
-    $seqno              = '';
-    $input_line_no      = 0;
-    $last_token         = ' ';
-    $last_type          = 'b';
-    $last_block_type    = '';
-    $last_seqno         = '';
-    $last_input_line_no = 0;
+    $rtokh = [ @{ $rLL->[0] } ];
+    $token = ' ';
+    $type  = 'b';
 
-    my $jmax = @{$rLL} - 1;
+    $rtokh->[_TOKEN_]         = $token;
+    $rtokh->[_TYPE_]          = $type;
+    $rtokh->[_TYPE_SEQUENCE_] = '';
+    $rtokh->[_LINE_INDEX_]    = 0;
 
     my ($ws);
 
@@ -2161,52 +2660,28 @@ sub set_whitespace_flags {
                 $closing_container_inside_ws{$sequence_number} = $ws_flag;
             }
         }
+        return;
     };
 
-    my $ws_opening_container_override = sub {
-        my ( $ws, $sequence_number ) = @_;
-        return $ws unless (%opening_container_inside_ws);
-        if ($sequence_number) {
-            my $ws_override = $opening_container_inside_ws{$sequence_number};
-            if ($ws_override) { $ws = $ws_override }
-        }
-        return $ws;
-    };
-
-    my $ws_closing_container_override = sub {
-        my ( $ws, $sequence_number ) = @_;
-        return $ws unless (%closing_container_inside_ws);
-        if ($sequence_number) {
-            my $ws_override = $closing_container_inside_ws{$sequence_number};
-            if ($ws_override) { $ws = $ws_override }
-        }
-        return $ws;
-    };
+    my ( $ws_1, $ws_2, $ws_3, $ws_4 );
 
     # main loop over all tokens to define the whitespace flags
     for ( my $j = 0 ; $j <= $jmax ; $j++ ) {
 
-        my $rtokh = $rLL->[$j];
-
-        # Set a default
-        $rwhitespace_flags->[$j] = WS_OPTIONAL;
-
-        if ( $rtokh->[_TYPE_] eq 'b' ) {
+        if ( $rLL->[$j]->[_TYPE_] eq 'b' ) {
+            $rwhitespace_flags->[$j] = WS_OPTIONAL;
             next;
         }
 
-        # set a default value, to be changed as needed
-        $ws                 = undef;
-        $last_token         = $token;
-        $last_type          = $type;
-        $last_block_type    = $block_type;
-        $last_seqno         = $seqno;
-        $last_input_line_no = $input_line_no;
-        $token              = $rtokh->[_TOKEN_];
-        $type               = $rtokh->[_TYPE_];
-        $block_type         = $rtokh->[_BLOCK_TYPE_];
-        $seqno              = $rtokh->[_TYPE_SEQUENCE_];
-        $input_line_no      = $rtokh->[_LINE_INDEX_];
+        $rtokh_last = $rtokh;
+        $last_token = $token;
+        $last_type  = $type;
+
+        $rtokh = $rLL->[$j];
+        $token = $rtokh->[_TOKEN_];
+        $type  = $rtokh->[_TYPE_];
+
+        $ws = undef;
 
         #---------------------------------------------------------------
         # Whitespace Rules Section 1:
@@ -2216,6 +2691,11 @@ sub set_whitespace_flags {
         #    /^[L\{\(\[]$/
         if ( $is_opening_type{$last_type} ) {
 
+            my $seqno           = $rtokh->[_TYPE_SEQUENCE_];
+            my $block_type      = $rblock_type_of_seqno->{$seqno};
+            my $last_seqno      = $rtokh_last->[_TYPE_SEQUENCE_];
+            my $last_block_type = $rblock_type_of_seqno->{$last_seqno};
+
             $j_tight_closing_paren = -1;
 
             # let us keep empty matched braces together: () {} []
@@ -2273,12 +2753,15 @@ sub set_whitespace_flags {
             }
 
             # check for special cases which override the above rules
-            $ws = $ws_opening_container_override->( $ws, $last_seqno );
+            if ( %opening_container_inside_ws && $last_seqno ) {
+                my $ws_override = $opening_container_inside_ws{$last_seqno};
+                if ($ws_override) { $ws = $ws_override }
+            }
 
-        }    # end setting space flag inside opening tokens
-        my $ws_1;
-        $ws_1 = $ws
-          if DEBUG_WHITE;
+            $ws_4 = $ws_3 = $ws_2 = $ws_1 = $ws
+              if DEBUG_WHITE;
+
+        } ## end setting space flag inside opening tokens
 
         #---------------------------------------------------------------
         # Whitespace Rules Section 2:
@@ -2288,6 +2771,7 @@ sub set_whitespace_flags {
         #   /[\}\)\]R]/
         if ( $is_closing_type{$type} ) {
 
+            my $seqno = $rtokh->[_TYPE_SEQUENCE_];
             if ( $j == $j_tight_closing_paren ) {
 
                 $j_tight_closing_paren = -1;
@@ -2298,6 +2782,7 @@ sub set_whitespace_flags {
                 if ( !defined($ws) ) {
 
                     my $tightness;
+                    my $block_type = $rblock_type_of_seqno->{$seqno};
                     if ( $type eq '}' && $token eq '}' && $block_type ) {
                         $tightness = $rOpts_block_brace_tightness;
                     }
@@ -2308,145 +2793,195 @@ sub set_whitespace_flags {
             }
 
             # check for special cases which override the above rules
-            $ws = $ws_closing_container_override->( $ws, $seqno );
-
-        }    # end setting space flag inside closing tokens
+            if ( %closing_container_inside_ws && $seqno ) {
+                my $ws_override = $closing_container_inside_ws{$seqno};
+                if ($ws_override) { $ws = $ws_override }
+            }
 
-        my $ws_2;
-        $ws_2 = $ws
-          if DEBUG_WHITE;
+            $ws_4 = $ws_3 = $ws_2 = $ws
+              if DEBUG_WHITE;
+        } ## end setting space flag inside closing tokens
 
         #---------------------------------------------------------------
         # Whitespace Rules Section 3:
-        # Use the binary rule table.
-        #---------------------------------------------------------------
-        if ( !defined($ws) ) {
-            $ws = $binary_ws_rules{$last_type}{$type};
-        }
-        my $ws_3;
-        $ws_3 = $ws
-          if DEBUG_WHITE;
-
-        #---------------------------------------------------------------
-        # Whitespace Rules Section 4:
         # Handle some special cases.
         #---------------------------------------------------------------
-        if ( $token eq '(' ) {
 
-            # This will have to be tweaked as tokenization changes.
-            # We usually want a space at '} (', for example:
-            # <<snippets/space1.in>>
-            #     map { 1 * $_; } ( $y, $M, $w, $d, $h, $m, $s );
-            #
-            # But not others:
-            #     &{ $_->[1] }( delete $_[$#_]{ $_->[0] } );
-            # At present, the above & block is marked as type L/R so this case
-            # won't go through here.
-            if ( $last_type eq '}' && $last_token ne ')' ) { $ws = WS_YES }
-
-            # NOTE: some older versions of Perl had occasional problems if
-            # spaces are introduced between keywords or functions and opening
-            # parens.  So the default is not to do this except is certain
-            # cases.  The current Perl seems to tolerate spaces.
-
-            # Space between keyword and '('
-            elsif ( $last_type eq 'k' ) {
-                $ws = WS_NO
-                  unless ( $rOpts_space_keyword_paren
-                    || $space_after_keyword{$last_token} );
-
-                # Set inside space flag if requested
-                $set_container_ws_by_keyword->( $last_token, $seqno );
-            }
-
-            # Space between function and '('
-            # -----------------------------------------------------
-            # 'w' and 'i' checks for something like:
-            #   myfun(    &myfun(   ->myfun(
-            # -----------------------------------------------------
-
-            # Note that at this point an identifier may still have a leading
-            # arrow, but the arrow will be split off during token respacing.
-            # After that, the token may become a bare word without leading
-            # arrow.  The point is, it is best to mark function call parens
-            # right here before that happens.
-            # Patch: added 'C' to prevent blinker, case b934, i.e. 'pi()'
-            # NOTE: this would be the place to allow spaces between repeated
-            # parens, like () () (), as in case c017, but I decided that would
-            # not be a good idea.
-            elsif (( $last_type =~ /^[wCUG]$/ )
-                || ( $last_type =~ /^[wi]$/ && $last_token =~ /^([\&]|->)/ ) )
-            {
-                $ws = $rOpts_space_function_paren ? WS_YES : WS_NO;
-                $set_container_ws_by_keyword->( $last_token, $seqno );
-                $ris_function_call_paren->{$seqno} = 1;
-            }
+        #    /^[L\{\(\[]$/
+        elsif ( $is_opening_type{$type} ) {
+
+            if ( $token eq '(' ) {
+
+                my $seqno = $rtokh->[_TYPE_SEQUENCE_];
+
+              # This will have to be tweaked as tokenization changes.
+              # We usually want a space at '} (', for example:
+              # <<snippets/space1.in>>
+              #     map { 1 * $_; } ( $y, $M, $w, $d, $h, $m, $s );
+              #
+              # But not others:
+              #     &{ $_->[1] }( delete $_[$#_]{ $_->[0] } );
+              # At present, the above & block is marked as type L/R so this case
+              # won't go through here.
+                if ( $last_type eq '}' && $last_token ne ')' ) { $ws = WS_YES }
+
+               # NOTE: some older versions of Perl had occasional problems if
+               # spaces are introduced between keywords or functions and opening
+               # parens.  So the default is not to do this except is certain
+               # cases.  The current Perl seems to tolerate spaces.
+
+                # Space between keyword and '('
+                elsif ( $last_type eq 'k' ) {
+                    $ws = WS_NO
+                      unless ( $rOpts_space_keyword_paren
+                        || $space_after_keyword{$last_token} );
+
+                    # Set inside space flag if requested
+                    $set_container_ws_by_keyword->( $last_token, $seqno );
+                }
+
+                # Space between function and '('
+                # -----------------------------------------------------
+                # 'w' and 'i' checks for something like:
+                #   myfun(    &myfun(   ->myfun(
+                # -----------------------------------------------------
+
+              # Note that at this point an identifier may still have a leading
+              # arrow, but the arrow will be split off during token respacing.
+              # After that, the token may become a bare word without leading
+              # arrow.  The point is, it is best to mark function call parens
+              # right here before that happens.
+              # Patch: added 'C' to prevent blinker, case b934, i.e. 'pi()'
+              # NOTE: this would be the place to allow spaces between repeated
+              # parens, like () () (), as in case c017, but I decided that would
+              # not be a good idea.
+                elsif (
+                       ( $last_type =~ /^[wCUG]$/ )
+                    || ( $last_type =~ /^[wi]$/ && $last_token =~ /^([\&]|->)/ )
+                  )
+                {
+                    $ws = $rOpts_space_function_paren ? WS_YES : WS_NO;
+                    $set_container_ws_by_keyword->( $last_token, $seqno );
+                    $ris_function_call_paren->{$seqno} = 1;
+                }
+
+               # space between something like $i and ( in <<snippets/space2.in>>
+               # for $i ( 0 .. 20 ) {
+               # FIXME: eventually, type 'i' could be split into multiple
+               # token types so this can be a hardwired rule.
+                elsif ( $last_type eq 'i' && $last_token =~ /^[\$\%\@]/ ) {
+                    $ws = WS_YES;
+                }
 
-            # space between something like $i and ( in <<snippets/space2.in>>
-            # for $i ( 0 .. 20 ) {
-            # FIXME: eventually, type 'i' could be split into multiple
-            # token types so this can be a hardwired rule.
-            elsif ( $last_type eq 'i' && $last_token =~ /^[\$\%\@]/ ) {
-                $ws = WS_YES;
+                # allow constant function followed by '()' to retain no space
+                elsif ($last_type eq 'C'
+                    && $rLL->[ $j + 1 ]->[_TOKEN_] eq ')' )
+                {
+                    $ws = WS_NO;
+                }
             }
 
-            # allow constant function followed by '()' to retain no space
-            elsif ($last_type eq 'C'
-                && $rLL->[ $j + 1 ]->[_TOKEN_] eq ')' )
-            {
-                $ws = WS_NO;
+            # patch for SWITCH/CASE: make space at ']{' optional
+            # since the '{' might begin a case or when block
+            elsif ( ( $token eq '{' && $type ne 'L' ) && $last_token eq ']' ) {
+                $ws = WS_OPTIONAL;
             }
-        }
 
-        # patch for SWITCH/CASE: make space at ']{' optional
-        # since the '{' might begin a case or when block
-        elsif ( ( $token eq '{' && $type ne 'L' ) && $last_token eq ']' ) {
-            $ws = WS_OPTIONAL;
-        }
+            # keep space between 'sub' and '{' for anonymous sub definition
+            if ( $type eq '{' ) {
+                if ( $last_token eq 'sub' ) {
+                    $ws = WS_YES;
+                }
+
+                # this is needed to avoid no space in '){'
+                if ( $last_token eq ')' && $token eq '{' ) { $ws = WS_YES }
 
-        # keep space between 'sub' and '{' for anonymous sub definition
-        if ( $type eq '{' ) {
-            if ( $last_token eq 'sub' ) {
-                $ws = WS_YES;
+                # avoid any space before the brace or bracket in something like
+                #  @opts{'a','b',...}
+                if ( $last_type eq 'i' && $last_token =~ /^\@/ ) {
+                    $ws = WS_NO;
+                }
             }
+        } ## end if ( $is_opening_type{$type} ) {
 
-            # this is needed to avoid no space in '){'
-            if ( $last_token eq ')' && $token eq '{' ) { $ws = WS_YES }
+        # Special checks for certain other types ...
+        # the hash '%is_special_ws_type' significantly speeds up this routine,
+        # but be sure to update it if a new check is added.
+        # Currently has types: qw(k w i C m - Q #)
+        elsif ( $is_special_ws_type{$type} ) {
+            if ( $type eq 'i' ) {
 
-            # avoid any space before the brace or bracket in something like
-            #  @opts{'a','b',...}
-            if ( $last_type eq 'i' && $last_token =~ /^\@/ ) {
-                $ws = WS_NO;
+                # never a space before ->
+                if ( substr( $token, 0, 2 ) eq '->' ) {
+                    $ws = WS_NO;
+                }
             }
-        }
 
-        elsif ( $type eq 'i' ) {
+            elsif ( $type eq 'k' ) {
 
-            # never a space before ->
-            if ( substr( $token, 0, 2 ) eq '->' ) {
-                $ws = WS_NO;
+                # Keywords 'for', 'foreach' are special cases for -kpit since
+                # the opening paren does not always immediately follow the
+                # keyword. So we have to search forward for the paren in this
+                # case.  I have limited the search to 10 tokens ahead, just in
+                # case somebody has a big file and no opening paren.  This
+                # should be enough for all normal code. Added the level check
+                # to fix b1236.
+                if (   $is_for_foreach{$token}
+                    && %keyword_paren_inner_tightness
+                    && defined( $keyword_paren_inner_tightness{$token} )
+                    && $j < $jmax )
+                {
+                    my $level = $rLL->[$j]->[_LEVEL_];
+                    my $jp    = $j;
+                    for ( my $inc = 1 ; $inc < 10 ; $inc++ ) {
+                        $jp++;
+                        last if ( $jp > $jmax );
+                        last if ( $rLL->[$jp]->[_LEVEL_] != $level );    # b1236
+                        next unless ( $rLL->[$jp]->[_TOKEN_] eq '(' );
+                        my $seqno_p = $rLL->[$jp]->[_TYPE_SEQUENCE_];
+                        $set_container_ws_by_keyword->( $token, $seqno_p );
+                        last;
+                    }
+                }
             }
-        }
 
-        # retain any space between '-' and bare word
-        elsif ( $type eq 'w' || $type eq 'C' ) {
-            $ws = WS_OPTIONAL if $last_type eq '-';
+            # retain any space between '-' and bare word
+            elsif ( $type eq 'w' || $type eq 'C' ) {
+                $ws = WS_OPTIONAL if $last_type eq '-';
+
+                # never a space before ->
+                if ( substr( $token, 0, 2 ) eq '->' ) {
+                    $ws = WS_NO;
+                }
+            }
 
-            # never a space before ->
-            if ( substr( $token, 0, 2 ) eq '->' ) {
-                $ws = WS_NO;
+            # retain any space between '-' and bare word; for example
+            # avoid space between 'USER' and '-' here: <<snippets/space2.in>>
+            #   $myhash{USER-NAME}='steve';
+            elsif ( $type eq 'm' || $type eq '-' ) {
+                $ws = WS_OPTIONAL if ( $last_type eq 'w' );
             }
-        }
 
-        # retain any space between '-' and bare word; for example
-        # avoid space between 'USER' and '-' here: <<snippets/space2.in>>
-        #   $myhash{USER-NAME}='steve';
-        elsif ( $type eq 'm' || $type eq '-' ) {
-            $ws = WS_OPTIONAL if ( $last_type eq 'w' );
-        }
+            # always space before side comment
+            elsif ( $type eq '#' ) { $ws = WS_YES if $j > 0 }
 
-        # always space before side comment
-        elsif ( $type eq '#' ) { $ws = WS_YES if $j > 0 }
+            # space_backslash_quote; RT #123774  <<snippets/rt123774.in>>
+            # allow a space between a backslash and single or double quote
+            # to avoid fooling html formatters
+            elsif ( $last_type eq '\\' && $type eq 'Q' && $token =~ /^[\"\']/ )
+            {
+                if ($rOpts_space_backslash_quote) {
+                    if ( $rOpts_space_backslash_quote == 1 ) {
+                        $ws = WS_OPTIONAL;
+                    }
+                    elsif ( $rOpts_space_backslash_quote == 2 ) { $ws = WS_YES }
+                    else { }    # shouldnt happen
+                }
+                else {
+                    $ws = WS_NO;
+                }
+            }
+        } ## end elsif ( $is_special_ws_type{$type} ...
 
         # always preserver whatever space was used after a possible
         # filehandle (except _) or here doc operator
@@ -2459,85 +2994,55 @@ sub set_whitespace_flags {
             $ws = WS_OPTIONAL;
         }
 
-        # space_backslash_quote; RT #123774  <<snippets/rt123774.in>>
-        # allow a space between a backslash and single or double quote
-        # to avoid fooling html formatters
-        elsif ( $last_type eq '\\' && $type eq 'Q' && $token =~ /^[\"\']/ ) {
-            if ($rOpts_space_backslash_quote) {
-                if ( $rOpts_space_backslash_quote == 1 ) {
-                    $ws = WS_OPTIONAL;
-                }
-                elsif ( $rOpts_space_backslash_quote == 2 ) { $ws = WS_YES }
-                else { }    # shouldnt happen
-            }
-            else {
-                $ws = WS_NO;
-            }
-        }
-        elsif ( $type eq 'k' ) {
-
-            # Keywords 'for', 'foreach' are special cases for -kpit since the
-            # opening paren does not always immediately follow the keyword. So
-            # we have to search forward for the paren in this case.  I have
-            # limited the search to 10 tokens ahead, just in case somebody
-            # has a big file and no opening paren.  This should be enough for
-            # all normal code.
-            if (   $is_for_foreach{$token}
-                && %keyword_paren_inner_tightness
-                && defined( $keyword_paren_inner_tightness{$token} )
-                && $j < $jmax )
-            {
-                my $jp = $j;
-                for ( my $inc = 1 ; $inc < 10 ; $inc++ ) {
-                    $jp++;
-                    last if ( $jp > $jmax );
-                    next unless ( $rLL->[$jp]->[_TOKEN_] eq '(' );
-                    my $seqno = $rLL->[$jp]->[_TYPE_SEQUENCE_];
-                    $set_container_ws_by_keyword->( $token, $seqno );
-                    last;
-                }
-            }
-        }
-
-        my $ws_4;
-        $ws_4 = $ws
+        $ws_4 = $ws_3 = $ws
           if DEBUG_WHITE;
 
-        #---------------------------------------------------------------
-        # Whitespace Rules Section 5:
-        # Apply default rules not covered above.
-        #---------------------------------------------------------------
-
-        # If we fall through to here, look at the pre-defined hash tables for
-        # the two tokens, and:
-        #  if (they are equal) use the common value
-        #  if (either is zero or undef) use the other
-        #  if (either is -1) use it
-        # That is,
-        # left  vs right
-        #  1    vs    1     -->  1
-        #  0    vs    0     -->  0
-        # -1    vs   -1     --> -1
-        #
-        #  0    vs   -1     --> -1
-        #  0    vs    1     -->  1
-        #  1    vs    0     -->  1
-        # -1    vs    0     --> -1
-        #
-        # -1    vs    1     --> -1
-        #  1    vs   -1     --> -1
         if ( !defined($ws) ) {
-            my $wl = $want_left_space{$type};
-            my $wr = $want_right_space{$last_type};
-            if ( !defined($wl) ) { $wl = 0 }
-            if ( !defined($wr) ) { $wr = 0 }
-            $ws = ( ( $wl == $wr ) || ( $wl == -1 ) || !$wr ) ? $wl : $wr;
-        }
 
-        if ( !defined($ws) ) {
-            $ws = 0;
-            write_diagnostics(
-                "WS flag is undefined for tokens $last_token $token\n");
+            #---------------------------------------------------------------
+            # Whitespace Rules Section 4:
+            # Use the binary rule table.
+            #---------------------------------------------------------------
+            $ws   = $binary_ws_rules{$last_type}{$type};
+            $ws_4 = $ws if DEBUG_WHITE;
+
+            #---------------------------------------------------------------
+            # Whitespace Rules Section 5:
+            # Apply default rules not covered above.
+            #---------------------------------------------------------------
+
+           # If we fall through to here, look at the pre-defined hash tables for
+           # the two tokens, and:
+           #  if (they are equal) use the common value
+           #  if (either is zero or undef) use the other
+           #  if (either is -1) use it
+           # That is,
+           # left  vs right
+           #  1    vs    1     -->  1
+           #  0    vs    0     -->  0
+           # -1    vs   -1     --> -1
+           #
+           #  0    vs   -1     --> -1
+           #  0    vs    1     -->  1
+           #  1    vs    0     -->  1
+           # -1    vs    0     --> -1
+           #
+           # -1    vs    1     --> -1
+           #  1    vs   -1     --> -1
+            if ( !defined($ws) ) {
+                my $wl = $want_left_space{$type};
+                my $wr = $want_right_space{$last_type};
+                if ( !defined($wl) ) {
+                    $ws = defined($wr) ? $wr : 0;
+                }
+                elsif ( !defined($wr) ) {
+                    $ws = $wl;
+                }
+                else {
+                    $ws =
+                      ( ( $wl == $wr ) || ( $wl == -1 ) || !$wr ) ? $wl : $wr;
+                }
+            }
         }
 
         # Treat newline as a whitespace. Otherwise, we might combine
@@ -2546,11 +3051,15 @@ sub set_whitespace_flags {
         #    my $msg = new Fax::Send
         #      -recipients => $to,
         #      -data => $data;
-        if ( $ws == 0 && $input_line_no != $last_input_line_no ) { $ws = 1 }
+        if (   $ws == 0
+            && $rtokh->[_LINE_INDEX_] != $rtokh_last->[_LINE_INDEX_] )
+        {
+            $ws = 1;
+        }
 
         $rwhitespace_flags->[$j] = $ws;
 
-        DEBUG_WHITE && do {
+        if (DEBUG_WHITE) {
             my $str = substr( $last_token, 0, 15 );
             $str .= ' ' x ( 16 - length($str) );
             if ( !defined($ws_1) ) { $ws_1 = "*" }
@@ -2559,7 +3068,10 @@ sub set_whitespace_flags {
             if ( !defined($ws_4) ) { $ws_4 = "*" }
             print STDOUT
 "NEW WHITE:  i=$j $str $last_type $type $ws_1 : $ws_2 : $ws_3 : $ws_4 : $ws \n";
-        };
+
+            # reset for next pass
+            $ws_1 = $ws_2 = $ws_3 = $ws_4 = undef;
+        }
     } ## end main loop
 
     if ( $rOpts->{'tight-secret-operators'} ) {
@@ -2615,10 +3127,14 @@ EOM
     my %essential_whitespace_filter_l2;
     my %essential_whitespace_filter_r2;
     my %is_type_with_space_before_bareword;
+    my %is_special_variable_char;
 
     BEGIN {
 
         my @q;
+
+        # NOTE: This hash is like the global %is_sort_map_grep, but it ignores
+        # grep aliases on purpose, since here we are looking parens, not braces
         @q = qw(sort grep map);
         @is_sort_grep_map{@q} = (1) x scalar(@q);
 
@@ -2669,6 +3185,12 @@ EOM
         @q = qw( Q & );
         @is_type_with_space_before_bareword{@q} = (1) x scalar(@q);
 
+        # These are the only characters which can (currently) form special
+        # variables, like $^W: (issue c066, c068).
+        @q =
+          qw{ ? A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ };
+        @{is_special_variable_char}{@q} = (1) x scalar(@q);
+
     }
 
     sub is_essential_whitespace {
@@ -2685,8 +3207,8 @@ EOM
 
         # Note2: The -mangle option causes large numbers of calls to this
         # routine and therefore is a good test. So if a change is made, be sure
-        # to run a large number of files with the -mangle option and check for
-        # differences.
+        # to use nytprof to profile with both old and reviesed coding using the
+        # -mangle option and check differences.
 
         my ( $tokenll, $typell, $tokenl, $typel, $tokenr, $typer ) = @_;
 
@@ -2747,8 +3269,7 @@ EOM
           # example: pom.caputo:
           # $vt100_compatible ? "\e[0;0H" : ('-' x 78 . "\n");
           || $typel eq 'n' && $tokenr eq '.'
-          || $typer eq 'n'
-          && $tokenl eq '.'
+          || $typer eq 'n' && $tokenl eq '.'
 
           # cases of a space before a bareword...
           || (
@@ -2781,11 +3302,14 @@ EOM
 
                 # keep a space between a token ending in '$' and any word;
                 # this caused trouble:  "die @$ if $@"
-                || $typel eq 'i' && $tokenl =~ /\$$/
+                ##|| $typel eq 'i' && $tokenl =~ /\$$/
+                || $typel eq 'i' && substr( $tokenl, -1, 1 ) eq '$'
 
                 # don't combine $$ or $# with any alphanumeric
                 # (testfile mangle.t with --mangle)
-                || $tokenl =~ /^\$[\$\#]$/
+                ##|| $tokenl =~ /^\$[\$\#]$/
+                || $tokenl eq '$$'
+                || $tokenl eq '$#'
 
             )
           )    ## end $tokenr_is_bareword
@@ -2809,7 +3333,8 @@ EOM
           || $typel eq 'w' && ( $tokenr eq '-' || $typer eq 'Q' )
 
           # perl is very fussy about spaces before <<
-          || $tokenr =~ /^\<\</
+          || substr( $tokenr, 0, 2 ) eq '<<'
+          ##|| $tokenr =~ /^\<\</
 
           # avoid combining tokens to create new meanings. Example:
           #     $a+ +$b must not become $a++$b
@@ -2858,9 +3383,11 @@ EOM
 
           # be careful with a space around ++ and --, to avoid ambiguity as to
           # which token it applies
-          || $typer  =~ /^(pp|mm)$/ && $tokenl !~ /^[\;\{\(\[]/
-          || $typel  =~ /^(\+\+|\-\-)$/
+          ##|| $typer =~ /^(pp|mm)$/     && $tokenl !~ /^[\;\{\(\[]/
+          || ( $typer eq 'pp' || $typer eq 'mm' ) && $tokenl !~ /^[\;\{\(\[]/
+          || ( $typel eq '++' || $typel eq '--' )
           && $tokenr !~ /^[\;\}\)\]]/
+          ##|| $typel =~ /^(\+\+|\-\-)$/ && $tokenr !~ /^[\;\}\)\]]/
 
           # need space after foreach my; for example, this will fail in
           # older versions of Perl:
@@ -2868,11 +3395,21 @@ EOM
           || (
             $tokenl eq 'my'
 
+            && substr( $tokenr, 0, 1 ) eq '$'
+            ##&& $tokenr =~ /^\$/
+
             #  /^(for|foreach)$/
             && $is_for_foreach{$tokenll}
-            && $tokenr =~ /^\$/
           )
 
+          # Keep space after like $^ if needed to avoid forming a different
+          # special variable (issue c068). For example:
+          #       my $aa = $^ ? "none" : "ok";
+          || ( $typel eq 'i'
+            && length($tokenl) == 2
+            && substr( $tokenl, 1, 1 ) eq '^'
+            && $is_special_variable_char{ substr( $tokenr, 0, 1 ) } )
+
           # We must be sure that a space between a ? and a quoted string
           # remains if the space before the ? remains.  [Loca.pm, lockarea]
           # ie,
@@ -3470,6 +4007,8 @@ EOM
         my $last_nonblank_token = $token;
         my $list_str            = $left_bond_strength{'?'};
 
+        my ( $bond_str_1, $bond_str_2, $bond_str_3, $bond_str_4 );
+
         my ( $block_type, $i_next, $i_next_nonblank, $next_nonblank_token,
             $next_nonblank_type, $next_token, $next_type,
             $total_nesting_depth, );
@@ -3486,6 +4025,7 @@ EOM
             # strength on both sides of a blank is the same
             if ( $type eq 'b' && $last_type ne 'b' ) {
                 $bond_strength_to_go[$i] = $bond_strength_to_go[ $i - 1 ];
+                $nobreak_to_go[$i] ||= $nobreak_to_go[ $i - 1 ]; # fix for b1257
                 next;
             }
 
@@ -3553,8 +4093,8 @@ EOM
             # section.
             if ( !defined($bsr) ) { $bsr = VERY_STRONG }
             if ( !defined($bsl) ) { $bsl = VERY_STRONG }
-            my $bond_str   = ( $bsr < $bsl ) ? $bsr : $bsl;
-            my $bond_str_1 = $bond_str;
+            my $bond_str = ( $bsr < $bsl ) ? $bsr : $bsl;
+            $bond_str_1 = $bond_str if (DEBUG_BOND);
 
             #---------------------------------------------------------------
             # Bond Strength Section 2:
@@ -3667,46 +4207,28 @@ EOM
                 $bond_str = NO_BREAK;
             }
 
-            # in older version of perl, use strict can cause problems with
-            # breaks before bare words following opening parens.  For example,
-            # this will fail under older versions if a break is made between
-            # '(' and 'MAIL': use strict; open( MAIL, "a long filename or
-            # command"); close MAIL;
-            if ( $type eq '{' ) {
+            # OLD COMMENT: In older version of perl, use strict can cause
+            # problems with breaks before bare words following opening parens.
+            # For example, this will fail under older versions if a break is
+            # made between '(' and 'MAIL':
 
-                if ( $token eq '(' && $next_nonblank_type eq 'w' ) {
+            # use strict; open( MAIL, "a long filename or command"); close MAIL;
 
-                    # but it's fine to break if the word is followed by a '=>'
-                    # or if it is obviously a sub call
-                    my $i_next_next_nonblank = $i_next_nonblank + 1;
-                    my $next_next_type = $types_to_go[$i_next_next_nonblank];
-                    if (   $next_next_type eq 'b'
-                        && $i_next_nonblank < $max_index_to_go )
-                    {
-                        $i_next_next_nonblank++;
-                        $next_next_type = $types_to_go[$i_next_next_nonblank];
-                    }
+            # NEW COMMENT: Third fix for b1213:
+            # This option does not seem to be needed any longer, and it can
+            # cause instabilities.  It can be turned off, but to minimize
+            # changes to existing formatting it is retained only in the case
+            # where the previous token was 'open' and there was no line break.
+            # Even this could eventually be removed if it causes instability.
+            if ( $type eq '{' ) {
 
-                    # We'll check for an old breakpoint and keep a leading
-                    # bareword if it was that way in the input file.
-                    # Presumably it was ok that way.  For example, the
-                    # following would remain unchanged:
-                    #
-                    # @months = (
-                    #   January,   February, March,    April,
-                    #   May,       June,     July,     August,
-                    #   September, October,  November, December,
-                    # );
-                    #
-                    # This should be sufficient:
-                    if (
-                        !$old_breakpoint_to_go[$i]
-                        && (   $next_next_type eq ','
-                            || $next_next_type eq '}' )
-                      )
-                    {
-                        $bond_str = NO_BREAK;
-                    }
+                if (   $token eq '('
+                    && $next_nonblank_type eq 'w'
+                    && $last_nonblank_type eq 'k'
+                    && $last_nonblank_token eq 'open'
+                    && !$old_breakpoint_to_go[$i] )
+                {
+                    $bond_str = NO_BREAK;
                 }
             }
 
@@ -3765,7 +4287,7 @@ EOM
                     && substr( $next_nonblank_token, 0, 1 ) eq '/' );
             }
 
-            my $bond_str_2 = $bond_str;
+            $bond_str_2 = $bond_str if (DEBUG_BOND);
 
             #---------------------------------------------------------------
             # End of hardwired rules
@@ -3788,6 +4310,20 @@ EOM
                 && $is_container_token{$next_nonblank_token} )
             {
                 $rtype = $next_nonblank_type . $next_nonblank_token;
+
+                # Alternate Fix #1 for issue b1299.  This version makes the
+                # decision as soon as possible.  See Alternate Fix #2 also.
+                # Do not separate a bareword identifier from its paren: b1299
+                # This is currently needed for stability because if the bareword
+                # gets separated from a preceding '->' and following '(' then
+                # the tokenizer may switch from type 'i' to type 'w'.  This
+                # patch will prevent this by keeping it adjacent to its '('.
+##              if (   $next_nonblank_token eq '('
+##                  && $ltype eq 'i'
+##                  && substr( $token, 0, 1 ) =~ /^\w$/ )
+##              {
+##                  $ltype = 'w';
+##              }
             }
 
             # apply binary rules which apply regardless of space between tokens
@@ -3806,7 +4342,8 @@ EOM
                 $bond_str           = NO_BREAK;
                 $tabulated_bond_str = $bond_str;
             }
-            my $bond_str_3 = $bond_str;
+
+            $bond_str_3 = $bond_str if (DEBUG_BOND);
 
             # If the hardwired rules conflict with the tabulated bond
             # strength then there is an inconsistency that should be fixed
@@ -3846,7 +4383,7 @@ EOM
 
             if ( $type eq ',' ) {
 
-                # add any bias set by sub scan_list at old comma break points
+                # add any bias set by sub break_lists at old comma break points
                 $bond_str += $bond_strength_to_go[$i];
 
             }
@@ -3882,7 +4419,8 @@ EOM
                     $bond_str += $bias{$right_key};
                 }
             }
-            my $bond_str_4 = $bond_str;
+
+            $bond_str_4 = $bond_str if (DEBUG_BOND);
 
             #---------------------------------------------------------------
             # Bond Strength Section 5:
@@ -3947,7 +4485,11 @@ EOM
                 $str .= ' ' x ( 16 - length($str) );
                 print STDOUT
 "BOND:  i=$i $str $type $next_nonblank_type depth=$total_nesting_depth strength=$bond_str_1 -> $bond_str_2 -> $bond_str_3 -> $bond_str_4 $bond_str -> $strength \n";
+
+                # reset for next pass
+                $bond_str_1 = $bond_str_2 = $bond_str_3 = $bond_str_4 = undef;
             };
+
         } ## end main loop
         return;
     } ## end sub set_bond_strengths
@@ -4229,10 +4771,12 @@ sub make_sub_matching_pattern {
     #  'sub ::m' is a named sub
     #  'sub' is an anonymous sub
     #  'sub:' is a label, not a sub
+    #  'sub :' is a label, not a sub   ( block type will be <sub:> )
+    #   sub'_ is a named sub           ( block type will be <sub '_> )
     #  'substr' is a keyword
-    $SUB_PATTERN    = '^sub\s+(::|\w)';    # match normal sub
-    $ASUB_PATTERN   = '^sub$';             # match anonymous sub
-    $ANYSUB_PATTERN = '^sub\b';            # match either type of sub
+    # So note that named subs always have a space after 'sub'
+    $SUB_PATTERN  = '^sub\s';    # match normal sub
+    $ASUB_PATTERN = '^sub$';     # match anonymous sub
 
     # Note (see also RT #133130): These patterns are used by
     # sub make_block_pattern, which is used for making most patterns.
@@ -4247,15 +4791,62 @@ sub make_sub_matching_pattern {
         $sub_alias_list =~ s/\s+/\|/g;
         $SUB_PATTERN    =~ s/sub/\($sub_alias_list\)/;
         $ASUB_PATTERN   =~ s/sub/\($sub_alias_list\)/;
-        $ANYSUB_PATTERN =~ s/sub/\($sub_alias_list\)/;
     }
     return;
 }
 
+sub make_bl_pattern {
+
+    # Set defaults lists to retain historical default behavior for -bl:
+    my $bl_list_string           = '*';
+    my $bl_exclusion_list_string = 'sort map grep eval asub';
+
+    if ( defined( $rOpts->{'brace-left-list'} )
+        && $rOpts->{'brace-left-list'} )
+    {
+        $bl_list_string = $rOpts->{'brace-left-list'};
+    }
+    if ( $bl_list_string =~ /\bsub\b/ ) {
+        $rOpts->{'opening-sub-brace-on-new-line'} ||=
+          $rOpts->{'opening-brace-on-new-line'};
+    }
+    if ( $bl_list_string =~ /\basub\b/ ) {
+        $rOpts->{'opening-anonymous-sub-brace-on-new-line'} ||=
+          $rOpts->{'opening-brace-on-new-line'};
+    }
+
+    $bl_pattern = make_block_pattern( '-bll', $bl_list_string );
+
+    # for -bl, a list with '*' turns on -sbl and -asbl
+    if ( $bl_pattern =~ /\.\*/ ) {
+        $rOpts->{'opening-sub-brace-on-new-line'} ||=
+          $rOpts->{'opening-brace-on-new-line'};
+        $rOpts->{'opening-anonymous-sub-brace-on-new-line'} ||=
+          $rOpts->{'opening-anonymous-brace-on-new-line'};
+    }
+
+    if ( defined( $rOpts->{'brace-left-exclusion-list'} )
+        && $rOpts->{'brace-left-exclusion-list'} )
+    {
+        $bl_exclusion_list_string = $rOpts->{'brace-left-exclusion-list'};
+        if ( $bl_exclusion_list_string =~ /\bsub\b/ ) {
+            $rOpts->{'opening-sub-brace-on-new-line'} = 0;
+        }
+        if ( $bl_exclusion_list_string =~ /\basub\b/ ) {
+            $rOpts->{'opening-anonymous-sub-brace-on-new-line'} = 0;
+        }
+    }
+
+    $bl_exclusion_pattern =
+      make_block_pattern( '-blxl', $bl_exclusion_list_string );
+    return;
+}
+
 sub make_bli_pattern {
 
     # default list of block types for which -bli would apply
     my $bli_list_string = 'if else elsif unless while for foreach do : sub';
+    my $bli_exclusion_list_string = ' ';
 
     if ( defined( $rOpts->{'brace-left-and-indent-list'} )
         && $rOpts->{'brace-left-and-indent-list'} )
@@ -4264,6 +4855,15 @@ sub make_bli_pattern {
     }
 
     $bli_pattern = make_block_pattern( '-blil', $bli_list_string );
+
+    if ( defined( $rOpts->{'brace-left-and-indent-exclusion-list'} )
+        && $rOpts->{'brace-left-and-indent-exclusion-list'} )
+    {
+        $bli_exclusion_list_string =
+          $rOpts->{'brace-left-and-indent-exclusion-list'};
+    }
+    $bli_exclusion_pattern =
+      make_block_pattern( '-blixl', $bli_exclusion_list_string );
     return;
 }
 
@@ -4374,6 +4974,11 @@ sub make_block_pattern {
             Warn("unrecognized block type $i after $abbrev, ignoring\n");
         }
     }
+
+    # Fix 2 for c091, prevent the pattern from matching an empty string
+    # '1 ' is an impossible block name.
+    if ( !@words ) { push @words, "1 " }
+
     my $pattern      = '(' . join( '|', @words ) . ')$';
     my $sub_patterns = "";
     if ( $seen{'sub'} ) {
@@ -4442,12 +5047,16 @@ sub make_closing_side_comment_prefix {
         if ( bad_pattern($test_csc_prefix_pattern) ) {
 
             # shouldn't happen..must have screwed up escaping, above
-            report_definite_bug();
-            Warn(
-"Program Error: the -cscp prefix '$csc_prefix' caused the invalid regex '$csc_prefix_pattern'\n"
-            );
+            if (DEVEL_MODE) {
+                Fault(<<EOM);
+Program Error: the -cscp prefix '$csc_prefix' caused the invalid regex '$csc_prefix_pattern'
+EOM
+            }
 
             # just warn and keep going with defaults
+            Warn(
+"Error: the -cscp prefix '$csc_prefix' caused the invalid regex '$csc_prefix_pattern'\n"
+            );
             Warn("Please consider using a simpler -cscp prefix\n");
             Warn("Using default -cscp instead; please check output\n");
         }
@@ -4467,37 +5076,145 @@ sub make_closing_side_comment_prefix {
 
 {    ## begin closure write_line
 
-    my $Last_line_had_side_comment;
-    my $In_format_skipping_section;
-    my $Saw_VERSION_in_this_file;
+    my $nesting_depth;
+
+    # Variables used by sub check_sequence_numbers:
+    my $last_seqno;
+    my %saw_opening_seqno;
+    my %saw_closing_seqno;
+    my $initial_seqno;
 
     sub initialize_write_line {
 
-        $Last_line_had_side_comment = 0;
-        $In_format_skipping_section = 0;
-        $Saw_VERSION_in_this_file   = 0;
+        $nesting_depth = undef;
+
+        $last_seqno        = SEQ_ROOT;
+        %saw_opening_seqno = ();
+        %saw_closing_seqno = ();
+
+        return;
+    }
+
+    sub check_sequence_numbers {
+
+        # Routine for checking sequence numbers.  This only needs to be
+        # done occasionally in DEVEL_MODE to be sure everything is working
+        # correctly.
+        my ( $rtokens, $rtoken_type, $rtype_sequence, $input_line_no ) = @_;
+        my $jmax = @{$rtokens} - 1;
+        return unless ( $jmax >= 0 );
+        foreach my $j ( 0 .. $jmax ) {
+            my $seqno = $rtype_sequence->[$j];
+            my $token = $rtokens->[$j];
+            my $type  = $rtoken_type->[$j];
+            my $err_msg =
+"Error at j=$j, line number $input_line_no, seqno='$seqno', type='$type', tok='$token':\n";
+
+            if ( !$seqno ) {
+
+           # Sequence numbers are generated for opening tokens, so every opening
+           # token should be sequenced.  Closing tokens will be unsequenced
+           # if they do not have a matching opening token.
+                if (   $is_opening_sequence_token{$token}
+                    && $type ne 'q'
+                    && $type ne 'Q' )
+                {
+                    Fault(
+                        <<EOM
+$err_msg Unexpected opening token without sequence number
+EOM
+                    );
+                }
+            }
+            else {
+
+                # Save starting seqno to identify sequence method:
+                # New method starts with 2 and has continuous numbering
+                # Old method starts with >2 and may have gaps
+                if ( !defined($initial_seqno) ) { $initial_seqno = $seqno }
+
+                if ( $is_opening_sequence_token{$token} ) {
+
+                    # New method should have continuous numbering
+                    if ( $initial_seqno == 2 && $seqno != $last_seqno + 1 ) {
+                        Fault(
+                            <<EOM
+$err_msg Unexpected opening sequence number: previous seqno=$last_seqno, but seqno= $seqno
+EOM
+                        );
+                    }
+                    $last_seqno = $seqno;
+
+                    # Numbers must be unique
+                    if ( $saw_opening_seqno{$seqno} ) {
+                        my $lno = $saw_opening_seqno{$seqno};
+                        Fault(
+                            <<EOM
+$err_msg Already saw an opening tokens at line $lno with this sequence number
+EOM
+                        );
+                    }
+                    $saw_opening_seqno{$seqno} = $input_line_no;
+                }
+
+                # only one closing item per seqno
+                elsif ( $is_closing_sequence_token{$token} ) {
+                    if ( $saw_closing_seqno{$seqno} ) {
+                        my $lno = $saw_closing_seqno{$seqno};
+                        Fault(
+                            <<EOM
+$err_msg Already saw a closing token with this seqno  at line $lno
+EOM
+                        );
+                    }
+                    $saw_closing_seqno{$seqno} = $input_line_no;
+
+                    # Every closing seqno must have an opening seqno
+                    if ( !$saw_opening_seqno{$seqno} ) {
+                        Fault(
+                            <<EOM
+$err_msg Saw a closing token but no opening token with this seqno
+EOM
+                        );
+                    }
+                }
 
+                # Sequenced items must be opening or closing
+                else {
+                    Fault(
+                        <<EOM
+$err_msg Unexpected token type with a sequence number
+EOM
+                    );
+                }
+            }
+        }
         return;
     }
 
     sub write_line {
 
-      # This routine originally received lines of code and immediately processed
-      # them.  That was efficient when memory was limited, but now it just saves
-      # the lines it receives.  They get processed all together after the last
-      # line is received.
+        # This routine receives lines one-by-one from the tokenizer and stores
+        # them in a format suitable for further processing.  After the last
+        # line has been sent, the tokenizer will call sub 'finish_formatting'
+        # to do the actual formatting.
 
-       # As tokenized lines are received they are converted to the format needed
-       # for the final formatting.
         my ( $self, $line_of_tokens_old ) = @_;
-        my $rLL           = $self->[_rLL_];
-        my $Klimit        = $self->[_Klimit_];
-        my $rlines_new    = $self->[_rlines_];
-        my $maximum_level = $self->[_maximum_level_];
+        my $rLL        = $self->[_rLL_];
+        my $Klimit     = $self->[_Klimit_];
+        my $rlines_new = $self->[_rlines_];
+
+        my $K_opening_container     = $self->[_K_opening_container_];
+        my $K_closing_container     = $self->[_K_closing_container_];
+        my $rdepth_of_opening_seqno = $self->[_rdepth_of_opening_seqno_];
+        my $rblock_type_of_seqno    = $self->[_rblock_type_of_seqno_];
+        my $rSS                     = $self->[_rSS_];
+        my $Iss_opening             = $self->[_Iss_opening_];
+        my $Iss_closing             = $self->[_Iss_closing_];
 
         my $Kfirst;
         my $line_of_tokens = {};
-        foreach my $key (
+        foreach (
             qw(
             _curly_brace_depth
             _ending_in_quote
@@ -4512,7 +5229,7 @@ sub make_closing_side_comment_prefix {
             )
           )
         {
-            $line_of_tokens->{$key} = $line_of_tokens_old->{$key};
+            $line_of_tokens->{$_} = $line_of_tokens_old->{$_};
         }
 
         # Data needed by Logger
@@ -4524,9 +5241,9 @@ sub make_closing_side_comment_prefix {
         # Needed to avoid trimming quotes
         $line_of_tokens->{_ended_in_blank_token} = undef;
 
-        my $line_type     = $line_of_tokens_old->{_line_type};
-        my $input_line_no = $line_of_tokens_old->{_line_number};
-        my $CODE_type     = "";
+        my $line_type   = $line_of_tokens_old->{_line_type};
+        my $line_number = $line_of_tokens_old->{_line_number};
+        my $CODE_type   = "";
         my $tee_output;
 
         # Handle line of non-code
@@ -4554,35 +5271,139 @@ sub make_closing_side_comment_prefix {
             my $jmax = @{$rtokens} - 1;
             if ( $jmax >= 0 ) {
                 $Kfirst = defined($Klimit) ? $Klimit + 1 : 0;
-                foreach my $j ( 0 .. $jmax ) {
 
-                 # Clip negative nesting depths to zero to avoid problems.
-                 # Negative values can occur in files with unbalanced containers
-                    my $slevel = $rslevels->[$j];
-                    if ( $slevel < 0 ) { $slevel = 0 }
+                DEVEL_MODE
+                  && check_sequence_numbers( $rtokens, $rtoken_type,
+                    $rtype_sequence, $line_number );
+
+                # Find the starting nesting depth ...
+                # It must be the value of variable 'level' of the first token
+                # because the nesting depth is used as a token tag in the
+                # vertical aligner and is compared to actual levels.
+                # So vertical alignment problems will occur with any other
+                # starting value.
+                if ( !defined($nesting_depth) ) {
+                    $nesting_depth = $rlevels->[0];
+                    $nesting_depth = 0 if ( $nesting_depth < 0 );
+                    $rdepth_of_opening_seqno->[SEQ_ROOT] = $nesting_depth - 1;
+                }
 
-                    if ( $rlevels->[$j] > $maximum_level ) {
-                        $maximum_level = $rlevels->[$j];
-                    }
+                foreach my $j ( 0 .. $jmax ) {
 
-                    # But do not clip the 'level' variable yet. We will do this
+                    # Do not clip the 'level' variable yet. We will do this
                     # later, in sub 'store_token_to_go'. The reason is that in
                     # files with level errors, the logic in 'weld_cuddled_else'
                     # uses a stack logic that will give bad welds if we clip
                     # levels here.
                     ## if ( $rlevels->[$j] < 0 ) { $rlevels->[$j] = 0 }
 
+                    # Handle tokens with sequence numbers ...
+                    my $seqno = $rtype_sequence->[$j];
+                    if ($seqno) {
+                        my $token = $rtokens->[$j];
+                        my $sign  = 1;
+                        if ( $is_opening_token{$token} ) {
+                            $K_opening_container->{$seqno} = @{$rLL};
+                            $rdepth_of_opening_seqno->[$seqno] = $nesting_depth;
+                            $nesting_depth++;
+
+                            # Save a sequenced block type at its opening token.
+                            # Note that unsequenced block types can occur in
+                            # unbalanced code with errors but are ignored here.
+                            if ( $rblock_type->[$j] ) {
+                                my $block_type = $rblock_type->[$j];
+                                $rblock_type_of_seqno->{$seqno} = $block_type;
+                                if ( substr( $block_type, 0, 3 ) eq 'sub'
+                                    || $rOpts_sub_alias_list )
+                                {
+                                    if ( $block_type =~ /$ASUB_PATTERN/ ) {
+                                        $self->[_ris_asub_block_]->{$seqno} = 1;
+                                    }
+                                    elsif ( $block_type =~ /$SUB_PATTERN/ ) {
+                                        $self->[_ris_sub_block_]->{$seqno} = 1;
+                                    }
+                                }
+                            }
+                        }
+                        elsif ( $is_closing_token{$token} ) {
+
+                            # The opening depth should always be defined, and
+                            # it should equal $nesting_depth-1.  To protect
+                            # against unforseen error conditions, however, we
+                            # will check this and fix things if necessary.  For
+                            # a test case see issue c055.
+                            my $opening_depth =
+                              $rdepth_of_opening_seqno->[$seqno];
+                            if ( !defined($opening_depth) ) {
+                                $opening_depth = $nesting_depth - 1;
+                                $opening_depth = 0 if ( $opening_depth < 0 );
+                                $rdepth_of_opening_seqno->[$seqno] =
+                                  $opening_depth;
+
+                                # This is not fatal but should not happen.  The
+                                # tokenizer generates sequence numbers
+                                # incrementally upon encountering each new
+                                # opening token, so every positive sequence
+                                # number should correspond to an opening token.
+                                if (DEVEL_MODE) {
+                                    Fault(<<EOM);
+No opening token seen for closing token = '$token' at seq=$seqno at depth=$opening_depth
+EOM
+                                }
+                            }
+                            $K_closing_container->{$seqno} = @{$rLL};
+                            $nesting_depth                 = $opening_depth;
+                            $sign                          = -1;
+                        }
+                        elsif ( $token eq '?' ) {
+                        }
+                        elsif ( $token eq ':' ) {
+                            $sign = -1;
+                        }
+
+                        # The only sequenced types output by the tokenizer are
+                        # the opening & closing containers and the ternary
+                        # types. So we would only get here if the tokenizer has
+                        # been changed to mark some other tokens with sequence
+                        # numbers, or if an error has been introduced in a
+                        # hash such as %is_opening_container
+                        else {
+                            if (DEVEL_MODE) {
+                                Fault(<<EOM);
+Unexpected sequenced token '$token' of type '$rtoken_type->[$j]', sequence=$seqno arrived from tokenizer.
+Expecting only opening or closing container tokens or ternary tokens with sequence numbers.
+EOM
+                            }
+                        }
+
+                        if ( $sign > 0 ) {
+                            $Iss_opening->[$seqno] = @{$rSS};
+
+                            # For efficiency, we find the maximum level of
+                            # opening tokens of any type.  The actual maximum
+                            # level will be that of their contents which is 1
+                            # greater.  That will be fixed in sub
+                            # 'finish_formatting'.
+                            my $level = $rlevels->[$j];
+                            if ( $level > $self->[_maximum_level_] ) {
+                                $self->[_maximum_level_]         = $level;
+                                $self->[_maximum_level_at_line_] = $line_number;
+                            }
+                        }
+                        else { $Iss_closing->[$seqno] = @{$rSS} }
+                        push @{$rSS}, $sign * $seqno;
+
+                    }
+
                     my @tokary;
                     @tokary[
-                      _TOKEN_,         _TYPE_,  _BLOCK_TYPE_,
-                      _TYPE_SEQUENCE_, _LEVEL_, _SLEVEL_,
-                      _CI_LEVEL_,      _LINE_INDEX_,
+                      _TOKEN_, _TYPE_,     _TYPE_SEQUENCE_,
+                      _LEVEL_, _CI_LEVEL_, _LINE_INDEX_,
                       ]
                       = (
-                        $rtokens->[$j],     $rtoken_type->[$j],
-                        $rblock_type->[$j], $rtype_sequence->[$j],
-                        $rlevels->[$j],     $slevel,
-                        $rci_levels->[$j],  $input_line_no - 1,
+                        $rtokens->[$j],    $rtoken_type->[$j],
+                        $seqno,            $rlevels->[$j],
+                        $rci_levels->[$j], $line_number - 1,
                       );
                     push @{$rLL}, \@tokary;
                 } ## end foreach my $j ( 0 .. $jmax )
@@ -4599,10 +5420,6 @@ sub make_closing_side_comment_prefix {
                 $line_of_tokens->{_nesting_tokens_0} = $rnesting_tokens->[0];
             } ## end if ( $jmax >= 0 )
 
-            $CODE_type =
-              $self->get_CODE_type( $line_of_tokens, $Kfirst, $Klimit,
-                $input_line_no );
-
             $tee_output ||=
                  $rOpts_tee_block_comments
               && $jmax == 0
@@ -4614,70 +5431,6 @@ sub make_closing_side_comment_prefix {
               && $Klimit > $Kfirst
               && $rLL->[$Klimit]->[_TYPE_] eq '#';
 
-            # Handle any requested side comment deletions. It is easier to get
-            # this done here rather than farther down the pipeline because IO
-            # lines take a different route, and because lines with deleted HSC
-            # become BL lines.  An since we are deleting now, we have to also
-            # handle any tee- requests before the side comments vanish.
-            my $delete_side_comment =
-                 $rOpts_delete_side_comments
-              && defined($Kfirst)
-              && $rLL->[$Klimit]->[_TYPE_] eq '#'
-              && ( $Klimit > $Kfirst || $CODE_type eq 'HSC' )
-              && (!$CODE_type
-                || $CODE_type eq 'HSC'
-                || $CODE_type eq 'IO'
-                || $CODE_type eq 'NIN' );
-
-            if (
-                   $rOpts_delete_closing_side_comments
-                && !$delete_side_comment
-                && defined($Kfirst)
-                && $Klimit > $Kfirst
-                && $rLL->[$Klimit]->[_TYPE_] eq '#'
-                && (  !$CODE_type
-                    || $CODE_type eq 'HSC'
-                    || $CODE_type eq 'IO'
-                    || $CODE_type eq 'NIN' )
-              )
-            {
-                my $token  = $rLL->[$Klimit]->[_TOKEN_];
-                my $K_m    = $Klimit - 1;
-                my $type_m = $rLL->[$K_m]->[_TYPE_];
-                if ( $type_m eq 'b' && $K_m > $Kfirst ) { $K_m-- }
-                my $last_nonblank_block_type = $rLL->[$K_m]->[_BLOCK_TYPE_];
-                if (   $token =~ /$closing_side_comment_prefix_pattern/
-                    && $last_nonblank_block_type =~
-                    /$closing_side_comment_list_pattern/ )
-                {
-                    $delete_side_comment = 1;
-                }
-            } ## end if ( $rOpts_delete_closing_side_comments...)
-
-            if ($delete_side_comment) {
-                pop @{$rLL};
-                $Klimit -= 1;
-                if (   $Klimit > $Kfirst
-                    && $rLL->[$Klimit]->[_TYPE_] eq 'b' )
-                {
-                    pop @{$rLL};
-                    $Klimit -= 1;
-                }
-
-                # The -io option outputs the line text, so we have to update
-                # the line text so that the comment does not reappear.
-                if ( $CODE_type eq 'IO' ) {
-                    my $line = "";
-                    foreach my $KK ( $Kfirst .. $Klimit ) {
-                        $line .= $rLL->[$KK]->[_TOKEN_];
-                    }
-                    $line_of_tokens->{_line_text} = $line . "\n";
-                }
-
-                # If we delete a hanging side comment the line becomes blank.
-                if ( $CODE_type eq 'HSC' ) { $CODE_type = 'BL' }
-            }
-
         } ## end if ( $line_type eq 'CODE')
 
         # Finish storing line variables
@@ -4690,154 +5443,332 @@ sub make_closing_side_comment_prefix {
         $line_of_tokens->{_rK_range}  = [ $Kfirst, $Klimit ];
         $line_of_tokens->{_code_type} = $CODE_type;
         $self->[_Klimit_]             = $Klimit;
-        $self->[_maximum_level_]      = $maximum_level;
 
         push @{$rlines_new}, $line_of_tokens;
         return;
     }
+} ## end closure write_line
 
-    sub get_CODE_type {
-        my ( $self, $line_of_tokens, $Kfirst, $Klast, $input_line_no ) = @_;
+#############################################
+# CODE SECTION 5: Pre-process the entire file
+#############################################
 
-        # We are looking at a line of code and setting a flag to
-        # describe any special processing that it requires
+sub finish_formatting {
 
-        # Possible CODE_types
-        # 'VB'  = Verbatim - line goes out verbatim (a quote)
-        # 'FS'  = Format Skipping - line goes out verbatim
-        # 'BL'  = Blank Line
-        # 'HSC' = Hanging Side Comment - fix this hanging side comment
-        # 'SBCX'= Static Block Comment Without Leading Space
-        # 'SBC' = Static Block Comment
-        # 'BC'  = Block Comment - an ordinary full line comment
-        # 'IO'  = Indent Only - line goes out unchanged except for indentation
-        # 'NIN' = No Internal Newlines - line does not get broken
-        # 'VER' = VERSION statement
-        # ''    = ordinary line of code with no restructions
+    my ( $self, $severe_error ) = @_;
 
-        my $rLL = $self->[_rLL_];
+    # The file has been tokenized and is ready to be formatted.
+    # All of the relevant data is stored in $self, ready to go.
 
-        my $CODE_type  = "";
-        my $input_line = $line_of_tokens->{_line_text};
-        my $jmax       = defined($Kfirst) ? $Klast - $Kfirst : -1;
+    # Check the maximum level. If it is extremely large we will give up and
+    # output the file verbatim.  Note that the actual maximum level is 1
+    # greater than the saved value, so we fix that here.
+    $self->[_maximum_level_] += 1;
+    my $maximum_level       = $self->[_maximum_level_];
+    my $maximum_table_index = $#maximum_line_length_at_level;
+    if ( !$severe_error && $maximum_level >= $maximum_table_index ) {
+        $severe_error ||= 1;
+        Warn(<<EOM);
+The maximum indentation level, $maximum_level, exceeds the builtin limit of $maximum_table_index.
+Something may be wrong; formatting will be skipped.
+EOM
+    }
 
-        my $is_block_comment = 0;
-        my $has_side_comment = 0;
+    # output file verbatim if severe error or no formatting requested
+    if ( $severe_error || $rOpts->{notidy} ) {
+        $self->dump_verbatim();
+        $self->wrapup();
+        return;
+    }
 
-        if ( $jmax >= 0 && $rLL->[$Klast]->[_TYPE_] eq '#' ) {
-            if   ( $jmax == 0 ) { $is_block_comment = 1; }
-            else                { $has_side_comment = 1 }
-        }
+    # Update the 'save_logfile' flag based to include any tokenization errors.
+    # We can save time by skipping logfile calls if it is not going to be saved.
+    my $logger_object = $self->[_logger_object_];
+    if ($logger_object) {
+        $self->[_save_logfile_] = $logger_object->get_save_logfile();
+    }
 
-        # Write line verbatim if we are in a formatting skip section
-        if ($In_format_skipping_section) {
+    $self->set_CODE_type();
 
-            # Note: extra space appended to comment simplifies pattern matching
-            if ( $is_block_comment
-                && ( $rLL->[$Kfirst]->[_TOKEN_] . " " ) =~
-                /$format_skipping_pattern_end/ )
-            {
-                $In_format_skipping_section = 0;
-                write_logfile_entry(
-                    "Line $input_line_no: Exiting format-skipping section\n");
-            }
-            $CODE_type = 'FS';
-            goto RETURN;
-        }
+    # Verify that the line hash does not have any unknown keys.
+    $self->check_line_hashes() if (DEVEL_MODE);
 
-        # Check for a continued quote..
-        if ( $line_of_tokens->{_starting_in_quote} ) {
+    # Make a pass through all tokens, adding or deleting any whitespace as
+    # required.  Also make any other changes, such as adding semicolons.
+    # All token changes must be made here so that the token data structure
+    # remains fixed for the rest of this iteration.
+    $self->respace_tokens();
 
-            # A line which is entirely a quote or pattern must go out
-            # verbatim.  Note: the \n is contained in $input_line.
-            if ( $jmax <= 0 ) {
-                if ( ( $input_line =~ "\t" ) ) {
-                    my $input_line_number = $line_of_tokens->{_line_number};
-                    $self->note_embedded_tab($input_line_number);
-                }
-                $CODE_type = 'VB';
-                goto RETURN;
-            }
-        }
+    $self->set_excluded_lp_containers();
 
-        # See if we are entering a formatting skip section
-        if (   $rOpts_format_skipping
-            && $is_block_comment
-            && ( $rLL->[$Kfirst]->[_TOKEN_] . " " ) =~
-            /$format_skipping_pattern_begin/ )
-        {
-            $In_format_skipping_section = 1;
-            write_logfile_entry(
-                "Line $input_line_no: Entering format-skipping section\n");
-            $CODE_type = 'FS';
-            goto RETURN;
-        }
+    $self->find_multiline_qw();
 
-        # ignore trailing blank tokens (they will get deleted later)
-        if ( $jmax > 0 && $rLL->[$Klast]->[_TYPE_] eq 'b' ) {
-            $jmax--;
-        }
+    $self->keep_old_line_breaks();
 
-        # blank line..
-        if ( $jmax < 0 ) {
-            $CODE_type = 'BL';
-            goto RETURN;
-        }
+    # Implement any welding needed for the -wn or -cb options
+    $self->weld_containers();
 
-        # see if this is a static block comment (starts with ## by default)
-        my $is_static_block_comment                       = 0;
-        my $is_static_block_comment_without_leading_space = 0;
-        if (   $is_block_comment
-            && $rOpts->{'static-block-comments'}
-            && $input_line =~ /$static_block_comment_pattern/ )
-        {
-            $is_static_block_comment = 1;
-            $is_static_block_comment_without_leading_space =
-              substr( $input_line, 0, 1 ) eq '#';
+    $self->collapsed_lengths()
+      if ( $rOpts_line_up_parentheses && $rOpts_extended_line_up_parentheses );
+
+    # Locate small nested blocks which should not be broken
+    $self->mark_short_nested_blocks();
+
+    $self->adjust_indentation_levels();
+
+    # Verify that the main token array looks OK.  If this ever causes a fault
+    # then place similar checks before the sub calls above to localize the
+    # problem.
+    $self->check_rLL("Before 'process_all_lines'") if (DEVEL_MODE);
+
+    # Finishes formatting and write the result to the line sink.
+    # Eventually this call should just change the 'rlines' data according to the
+    # new line breaks and then return so that we can do an internal iteration
+    # before continuing with the next stages of formatting.
+    $self->process_all_lines();
+
+    # A final routine to tie up any loose ends
+    $self->wrapup();
+    return;
+}
+
+sub set_CODE_type {
+    my ($self) = @_;
+
+    # This routine performs two tasks:
+
+    # TASK 1: Examine each line of code and set a flag '$CODE_type' to describe
+    # any special processing that it requires.
+
+    # TASK 2: Delete side comments if requested.
+
+    my $rLL                  = $self->[_rLL_];
+    my $Klimit               = $self->[_Klimit_];
+    my $rlines               = $self->[_rlines_];
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
+
+    my $rOpts_format_skipping_begin = $rOpts->{'format-skipping-begin'};
+    my $rOpts_format_skipping_end   = $rOpts->{'format-skipping-end'};
+    my $rOpts_static_block_comment_prefix =
+      $rOpts->{'static-block-comment-prefix'};
+
+    # Remember indexes of lines with side comments
+    my @ix_side_comments;
+
+    my $In_format_skipping_section = 0;
+    my $Saw_VERSION_in_this_file   = 0;
+    my $has_side_comment           = 0;
+    my ( $Kfirst, $Klast );
+    my $CODE_type;
+
+    #------------------------------
+    # TASK 1: Loop to set CODE_type
+    #------------------------------
+
+    # Possible CODE_types
+    # 'VB'  = Verbatim - line goes out verbatim (a quote)
+    # 'FS'  = Format Skipping - line goes out verbatim
+    # 'BL'  = Blank Line
+    # 'HSC' = Hanging Side Comment - fix this hanging side comment
+    # 'SBCX'= Static Block Comment Without Leading Space
+    # 'SBC' = Static Block Comment
+    # 'BC'  = Block Comment - an ordinary full line comment
+    # 'IO'  = Indent Only - line goes out unchanged except for indentation
+    # 'NIN' = No Internal Newlines - line does not get broken
+    # 'VER' = VERSION statement
+    # ''    = ordinary line of code with no restructions
+
+    my $ix_line = -1;
+    foreach my $line_of_tokens ( @{$rlines} ) {
+        $ix_line++;
+        my $input_line_no = $line_of_tokens->{_line_number};
+        my $line_type     = $line_of_tokens->{_line_type};
+
+        my $Last_line_had_side_comment = $has_side_comment;
+        if ($has_side_comment) {
+            push @ix_side_comments, $ix_line - 1;
         }
+        $has_side_comment = 0;
 
-        # Check for comments which are line directives
-        # Treat exactly as static block comments without leading space
-        # reference: perlsyn, near end, section Plain Old Comments (Not!)
-        # example: '# line 42 "new_filename.plx"'
-        if (
-               $is_block_comment
-            && $input_line =~ /^\#   \s*
-                               line \s+ (\d+)   \s*
-                               (?:\s("?)([^"]+)\2)? \s*
-                               $/x
-          )
-        {
-            $is_static_block_comment                       = 1;
-            $is_static_block_comment_without_leading_space = 1;
+        next unless ( $line_type eq 'CODE' );
+
+        my $Klast_prev = $Klast;
+
+        my $rK_range = $line_of_tokens->{_rK_range};
+        ( $Kfirst, $Klast ) = @{$rK_range};
+
+        my $last_CODE_type = $CODE_type;
+        $CODE_type = "";
+
+        my $input_line = $line_of_tokens->{_line_text};
+        my $jmax       = defined($Kfirst) ? $Klast - $Kfirst : -1;
+
+        my $is_block_comment = 0;
+        if ( $jmax >= 0 && $rLL->[$Klast]->[_TYPE_] eq '#' ) {
+            if   ( $jmax == 0 ) { $is_block_comment = 1; }
+            else                { $has_side_comment = 1 }
+        }
+
+        # Write line verbatim if we are in a formatting skip section
+        if ($In_format_skipping_section) {
+
+            # Note: extra space appended to comment simplifies pattern matching
+            if (
+                $is_block_comment
+
+                # optional fast pre-check
+                && ( substr( $rLL->[$Kfirst]->[_TOKEN_], 0, 4 ) eq '#>>>'
+                    || $rOpts_format_skipping_end )
+
+                && ( $rLL->[$Kfirst]->[_TOKEN_] . " " ) =~
+                /$format_skipping_pattern_end/
+              )
+            {
+                $In_format_skipping_section = 0;
+                write_logfile_entry(
+                    "Line $input_line_no: Exiting format-skipping section\n");
+            }
+            $CODE_type = 'FS';
+            goto NEXT;
         }
 
-        # look for hanging side comment
+        # Check for a continued quote..
+        if ( $line_of_tokens->{_starting_in_quote} ) {
+
+            # A line which is entirely a quote or pattern must go out
+            # verbatim.  Note: the \n is contained in $input_line.
+            if ( $jmax <= 0 ) {
+                if ( ( $input_line =~ "\t" ) ) {
+                    my $input_line_number = $line_of_tokens->{_line_number};
+                    $self->note_embedded_tab($input_line_number);
+                }
+                $CODE_type = 'VB';
+                goto NEXT;
+            }
+        }
+
+        # See if we are entering a formatting skip section
         if (
-               $is_block_comment
-            && $Last_line_had_side_comment  # last line had side comment
-            && $input_line =~ /^\s/         # there is some leading space
-            && !$is_static_block_comment    # do not make static comment hanging
-            && $rOpts->{'hanging-side-comments'}    # user is allowing
-                                                    # hanging side comments
-                                                    # like this
+            $is_block_comment
+
+            # optional fast pre-check
+            && ( substr( $rLL->[$Kfirst]->[_TOKEN_], 0, 4 ) eq '#<<<'
+                || $rOpts_format_skipping_begin )
+
+            && $rOpts_format_skipping
+            && ( $rLL->[$Kfirst]->[_TOKEN_] . " " ) =~
+            /$format_skipping_pattern_begin/
           )
         {
-            $has_side_comment = 1;
-            $CODE_type        = 'HSC';
-            goto RETURN;
+            $In_format_skipping_section = 1;
+            write_logfile_entry(
+                "Line $input_line_no: Entering format-skipping section\n");
+            $CODE_type = 'FS';
+            goto NEXT;
+        }
+
+        # ignore trailing blank tokens (they will get deleted later)
+        if ( $jmax > 0 && $rLL->[$Klast]->[_TYPE_] eq 'b' ) {
+            $jmax--;
+        }
+
+        # blank line..
+        if ( $jmax < 0 ) {
+            $CODE_type = 'BL';
+            goto NEXT;
         }
 
-        # Handle a block (full-line) comment..
+        # Handle comments
         if ($is_block_comment) {
 
-            if ($is_static_block_comment_without_leading_space) {
-                $CODE_type = 'SBCX';
-                goto RETURN;
+            # see if this is a static block comment (starts with ## by default)
+            my $is_static_block_comment = 0;
+            my $no_leading_space        = substr( $input_line, 0, 1 ) eq '#';
+            if (
+
+                # optional fast pre-check
+                (
+                    substr( $rLL->[$Kfirst]->[_TOKEN_], 0, 2 ) eq '##'
+                    || $rOpts_static_block_comment_prefix
+                )
+
+                && $rOpts_static_block_comments
+                && $input_line =~ /$static_block_comment_pattern/
+              )
+            {
+                $is_static_block_comment = 1;
+            }
+
+            # Check for comments which are line directives
+            # Treat exactly as static block comments without leading space
+            # reference: perlsyn, near end, section Plain Old Comments (Not!)
+            # example: '# line 42 "new_filename.plx"'
+            if (
+                   $no_leading_space
+                && $input_line =~ /^\#   \s*
+                           line \s+ (\d+)   \s*
+                           (?:\s("?)([^"]+)\2)? \s*
+                           $/x
+              )
+            {
+                $is_static_block_comment = 1;
+            }
+
+            # look for hanging side comment ...
+            if (
+                $Last_line_had_side_comment    # last line had side comment
+                && !$no_leading_space          # there is some leading space
+                && !
+                $is_static_block_comment    # do not make static comment hanging
+              )
+            {
+
+                #  continuing an existing HSC chain?
+                if ( $last_CODE_type eq 'HSC' ) {
+                    $has_side_comment = 1;
+                    $CODE_type        = 'HSC';
+                    goto NEXT;
+                }
+
+                #  starting a new HSC chain?
+                elsif (
+
+                    $rOpts->{'hanging-side-comments'}    # user is allowing
+                                                         # hanging side comments
+                                                         # like this
+
+                    && ( defined($Klast_prev) && $Klast_prev > 1 )
+
+                    # and the previous side comment was not static (issue c070)
+                    && !(
+                           $rOpts->{'static-side-comments'}
+                        && $rLL->[$Klast_prev]->[_TOKEN_] =~
+                        /$static_side_comment_pattern/
+                    )
+
+                  )
+                {
+
+                    # and it is not a closing side comment (issue c070).
+                    my $K_penult = $Klast_prev - 1;
+                    $K_penult -= 1 if ( $rLL->[$K_penult]->[_TYPE_] eq 'b' );
+                    my $follows_csc =
+                      (      $rLL->[$K_penult]->[_TOKEN_] eq '}'
+                          && $rLL->[$K_penult]->[_TYPE_] eq '}'
+                          && $rLL->[$Klast_prev]->[_TOKEN_] =~
+                          /$closing_side_comment_prefix_pattern/ );
+
+                    if ( !$follows_csc ) {
+                        $has_side_comment = 1;
+                        $CODE_type        = 'HSC';
+                        goto NEXT;
+                    }
+                }
             }
-            elsif ($is_static_block_comment) {
-                $CODE_type = 'SBC';
-                goto RETURN;
+
+            if ($is_static_block_comment) {
+                $CODE_type = $no_leading_space ? 'SBCX' : 'SBC';
+                goto NEXT;
             }
             elsif ($Last_line_had_side_comment
                 && !$rOpts_maximum_consecutive_blank_lines
@@ -4848,11 +5779,11 @@ sub make_closing_side_comment_prefix {
                 # cannot be inserted.  There is related code in sub
                 # 'process_line_of_CODE'
                 $CODE_type = 'SBCX';
-                goto RETURN;
+                goto NEXT;
             }
             else {
                 $CODE_type = 'BC';
-                goto RETURN;
+                goto NEXT;
             }
         }
 
@@ -4860,12 +5791,12 @@ sub make_closing_side_comment_prefix {
 
         if ($rOpts_indent_only) {
             $CODE_type = 'IO';
-            goto RETURN;
+            goto NEXT;
         }
 
         if ( !$rOpts_add_newlines ) {
             $CODE_type = 'NIN';
-            goto RETURN;
+            goto NEXT;
         }
 
         #   Patch needed for MakeMaker.  Do not break a statement
@@ -4900,81 +5831,113 @@ sub make_closing_side_comment_prefix {
 
             # This code type has lower priority than others
             $CODE_type = 'VER';
-            goto RETURN;
+            goto NEXT;
         }
 
-      RETURN:
-        $Last_line_had_side_comment = $has_side_comment;
-        return $CODE_type;
+      NEXT:
+        $line_of_tokens->{_code_type} = $CODE_type;
     }
 
-} ## end closure write_line
-
-#############################################
-# CODE SECTION 5: Pre-process the entire file
-#############################################
-
-sub finish_formatting {
-
-    my ( $self, $severe_error ) = @_;
+    if ($has_side_comment) {
+        push @ix_side_comments, $ix_line;
+    }
 
-    # The file has been tokenized and is ready to be formatted.
-    # All of the relevant data is stored in $self, ready to go.
+    return
+      if ( !$rOpts_delete_side_comments
+        && !$rOpts_delete_closing_side_comments );
 
-    # Check the maximum level. If it is extremely large we will
-    # give up and output the file verbatim.
-    my $maximum_level       = $self->[_maximum_level_];
-    my $maximum_table_index = $#maximum_line_length_at_level;
-    if ( !$severe_error && $maximum_level > $maximum_table_index ) {
-        $severe_error ||= 1;
-        Warn(<<EOM);
-The maximum indentation level, $maximum_level, exceeds the builtin limit of $maximum_table_index.
-Something may be wrong; formatting will be skipped. 
-EOM
-    }
+    #-------------------------------------
+    # TASK 2: Loop to delete side comments
+    #-------------------------------------
 
-    # output file verbatim if severe error or no formatting requested
-    if ( $severe_error || $rOpts->{notidy} ) {
-        $self->dump_verbatim();
-        $self->wrapup();
-        return;
-    }
+    # Handle any requested side comment deletions. It is easier to get
+    # this done here rather than farther down the pipeline because IO
+    # lines take a different route, and because lines with deleted HSC
+    # become BL lines.  We have already handled any tee requests in sub
+    # getline, so it is safe to delete side comments now.
 
-    # Update the 'save_logfile' flag based to include any tokenization errors.
-    # We can save time by skipping logfile calls if it is not going to be saved.
-    my $logger_object = $self->[_logger_object_];
-    if ($logger_object) {
-        $self->[_save_logfile_] = $logger_object->get_save_logfile();
-    }
+    # Also, we can get this done efficiently here.
 
-    # Make a pass through all tokens, adding or deleting any whitespace as
-    # required.  Also make any other changes, such as adding semicolons.
-    # All token changes must be made here so that the token data structure
-    # remains fixed for the rest of this iteration.
-    $self->respace_tokens();
+    foreach my $ix (@ix_side_comments) {
+        my $line_of_tokens = $rlines->[$ix];
+        my $line_type      = $line_of_tokens->{_line_type};
 
-    $self->find_multiline_qw();
+        # This fault shouldn't happen because we only saved CODE lines with
+        # side comments in the TASK 1 loop above.
+        if ( $line_type ne 'CODE' ) {
+            if (DEVEL_MODE) {
+                Fault(<<EOM);
+Hit unexpected line_type = '$line_type' while deleting side comments, should be 'CODE'
+EOM
+            }
+            next;
+        }
 
-    $self->keep_old_line_breaks();
+        my $CODE_type = $line_of_tokens->{_code_type};
+        my $rK_range  = $line_of_tokens->{_rK_range};
+        my ( $Kfirst, $Klast ) = @{$rK_range};
+        my $delete_side_comment =
+             $rOpts_delete_side_comments
+          && defined($Kfirst)
+          && $rLL->[$Klast]->[_TYPE_] eq '#'
+          && ( $Klast > $Kfirst || $CODE_type eq 'HSC' )
+          && (!$CODE_type
+            || $CODE_type eq 'HSC'
+            || $CODE_type eq 'IO'
+            || $CODE_type eq 'NIN' );
 
-    # Implement any welding needed for the -wn or -cb options
-    $self->weld_containers();
+        if (
+               $rOpts_delete_closing_side_comments
+            && !$delete_side_comment
+            && defined($Kfirst)
+            && $Klast > $Kfirst
+            && $rLL->[$Klast]->[_TYPE_] eq '#'
+            && (  !$CODE_type
+                || $CODE_type eq 'HSC'
+                || $CODE_type eq 'IO'
+                || $CODE_type eq 'NIN' )
+          )
+        {
+            my $token  = $rLL->[$Klast]->[_TOKEN_];
+            my $K_m    = $Klast - 1;
+            my $type_m = $rLL->[$K_m]->[_TYPE_];
+            if ( $type_m eq 'b' && $K_m > $Kfirst ) { $K_m-- }
+            my $seqno_m = $rLL->[$K_m]->[_TYPE_SEQUENCE_];
+            if ($seqno_m) {
+                my $block_type_m = $rblock_type_of_seqno->{$seqno_m};
+                if (   $block_type_m
+                    && $token        =~ /$closing_side_comment_prefix_pattern/
+                    && $block_type_m =~ /$closing_side_comment_list_pattern/ )
+                {
+                    $delete_side_comment = 1;
+                }
+            }
+        } ## end if ( $rOpts_delete_closing_side_comments...)
 
-    # Locate small nested blocks which should not be broken
-    $self->mark_short_nested_blocks();
+        if ($delete_side_comment) {
 
-    $self->adjust_indentation_levels();
+            # We are actually just changing the side comment to a blank.
+            # This may produce multiple blanks in a row, but sub respace_tokens
+            # will check for this and fix it.
+            $rLL->[$Klast]->[_TYPE_]  = 'b';
+            $rLL->[$Klast]->[_TOKEN_] = ' ';
 
-    $self->set_excluded_lp_containers();
+            # The -io option outputs the line text, so we have to update
+            # the line text so that the comment does not reappear.
+            if ( $CODE_type eq 'IO' ) {
+                my $line = "";
+                foreach my $KK ( $Kfirst .. $Klast - 1 ) {
+                    $line .= $rLL->[$KK]->[_TOKEN_];
+                }
+                $line =~ s/\s+$//;
+                $line_of_tokens->{_line_text} = $line . "\n";
+            }
 
-    # Finishes formatting and write the result to the line sink.
-    # Eventually this call should just change the 'rlines' data according to the
-    # new line breaks and then return so that we can do an internal iteration
-    # before continuing with the next stages of formatting.
-    $self->process_all_lines();
+            # If we delete a hanging side comment the line becomes blank.
+            if ( $CODE_type eq 'HSC' ) { $line_of_tokens->{_code_type} = 'BL' }
+        }
+    }
 
-    # A final routine to tie up any loose ends
-    $self->wrapup();
     return;
 }
 
@@ -4990,8 +5953,13 @@ sub dump_verbatim {
 
 my %wU;
 my %wiq;
+my %is_wit;
+my %is_sigil;
 my %is_nonlist_keyword;
 my %is_nonlist_type;
+my %is_special_check_type;
+my %is_s_y_m_slash;
+my %is_unexpected_equals;
 
 BEGIN {
 
@@ -5002,6 +5970,12 @@ BEGIN {
     @q = qw(w i q Q G C Z);
     @{wiq}{@q} = (1) x scalar(@q);
 
+    @q = qw(w i t);
+    @{is_wit}{@q} = (1) x scalar(@q);
+
+    @q = qw($ & % * @);
+    @{is_sigil}{@q} = (1) x scalar(@q);
+
     # Parens following these keywords will not be marked as lists. Note that
     # 'for' is not included and is handled separately, by including 'f' in the
     # hash %is_counted_type, since it may or may not be a c-style for loop.
@@ -5012,6 +5986,12 @@ BEGIN {
     @q = qw( && || );
     @is_nonlist_type{@q} = (1) x scalar(@q);
 
+    @q = qw( s y m / );
+    @is_s_y_m_slash{@q} = (1) x scalar(@q);
+
+    @q = qw( = == != );
+    @is_unexpected_equals{@q} = (1) x scalar(@q);
+
 }
 
 sub respace_tokens {
@@ -5066,9 +6046,15 @@ sub respace_tokens {
     my $depth_next             = 0;
     my $depth_next_max         = 0;
 
-    my $K_closing_container       = $self->[_K_closing_container_];
+    # Note that $K_opening_container and $K_closing_container have values
+    # defined in sub get_line() for the previous K indexes.  They were needed
+    # in case option 'indent-only' was set, and we didn't get here. We no longer
+    # need those and will eliminate them now to avoid any possible mixing of
+    # old and new values.
+    my $K_opening_container = $self->[_K_opening_container_] = {};
+    my $K_closing_container = $self->[_K_closing_container_] = {};
+
     my $K_closing_ternary         = $self->[_K_closing_ternary_];
-    my $K_opening_container       = $self->[_K_opening_container_];
     my $K_opening_ternary         = $self->[_K_opening_ternary_];
     my $rK_phantom_semicolons     = $self->[_rK_phantom_semicolons_];
     my $rchildren_of_seqno        = $self->[_rchildren_of_seqno_];
@@ -5087,12 +6073,13 @@ sub respace_tokens {
     my $roverride_cab3            = $self->[_roverride_cab3_];
     my $rparent_of_seqno          = $self->[_rparent_of_seqno_];
     my $rtype_count_by_seqno      = $self->[_rtype_count_by_seqno_];
+    my $rblock_type_of_seqno      = $self->[_rblock_type_of_seqno_];
 
-    my $last_nonblank_type       = ';';
-    my $last_nonblank_token      = ';';
-    my $last_nonblank_block_type = '';
-    my $nonblank_token_count     = 0;
-    my $last_nonblank_token_lx   = 0;
+    my $last_nonblank_code_type       = ';';
+    my $last_nonblank_code_token      = ';';
+    my $last_nonblank_block_type      = '';
+    my $last_last_nonblank_code_type  = ';';
+    my $last_last_nonblank_code_token = ';';
 
     my %K_first_here_doc_by_seqno;
 
@@ -5110,39 +6097,42 @@ sub respace_tokens {
         # This will be the index of this item in the new array
         my $KK_new = @{$rLL_new};
 
-        my $type     = $item->[_TYPE_];
-        my $is_blank = $type eq 'b';
+        my $type       = $item->[_TYPE_];
+        my $is_blank   = $type eq 'b';
+        my $block_type = "";
 
-        # Do not output consecutive blanks. This should not happen, but
-        # is worth checking because later routines make this assumption.
+        # Do not output consecutive blanks. This situation should have been
+        # prevented earlier, but it is worth checking because later routines
+        # make this assumption.
         if ( $is_blank && $KK_new && $rLL_new->[-1]->[_TYPE_] eq 'b' ) {
             return;
         }
 
         # check for a sequenced item (i.e., container or ?/:)
         my $type_sequence = $item->[_TYPE_SEQUENCE_];
+        my $token         = $item->[_TOKEN_];
         if ($type_sequence) {
 
-            my $token = $item->[_TOKEN_];
             if ( $is_opening_token{$token} ) {
 
                 $K_opening_container->{$type_sequence} = $KK_new;
+                $block_type = $rblock_type_of_seqno->{$type_sequence};
 
                 # Fix for case b1100: Count a line ending in ', [' as having
                 # a line-ending comma.  Otherwise, these commas can be hidden
                 # with something like --opening-square-bracket-right
-                if (   $last_nonblank_type eq ','
+                if (   $last_nonblank_code_type eq ','
                     && $Ktoken_vars == $Klast_old_code
                     && $Ktoken_vars > $Kfirst_old )
                 {
                     $rlec_count_by_seqno->{$type_sequence}++;
                 }
 
-                if (   $last_nonblank_type eq '='
-                    || $last_nonblank_type eq '=>' )
+                if (   $last_nonblank_code_type eq '='
+                    || $last_nonblank_code_type eq '=>' )
                 {
                     $ris_assigned_structure->{$type_sequence} =
-                      $last_nonblank_type;
+                      $last_nonblank_code_type;
                 }
 
                 my $seqno_parent = $seqno_stack{ $depth_next - 1 };
@@ -5160,18 +6150,19 @@ sub respace_tokens {
             elsif ( $is_closing_token{$token} ) {
 
                 $K_closing_container->{$type_sequence} = $KK_new;
+                $block_type = $rblock_type_of_seqno->{$type_sequence};
 
                 # Do not include terminal commas in counts
-                if (   $last_nonblank_type eq ','
-                    || $last_nonblank_type eq '=>' )
+                if (   $last_nonblank_code_type eq ','
+                    || $last_nonblank_code_type eq '=>' )
                 {
                     my $seqno = $seqno_stack{ $depth_next - 1 };
                     if ($seqno) {
-                        $rtype_count_by_seqno->{$seqno}->{$last_nonblank_type}
-                          --;
+                        $rtype_count_by_seqno->{$seqno}
+                          ->{$last_nonblank_code_type}--;
 
                         if (   $Ktoken_vars == $Kfirst_old
-                            && $last_nonblank_type eq ','
+                            && $last_nonblank_code_type eq ','
                             && $rlec_count_by_seqno->{$seqno} )
                         {
                             $rlec_count_by_seqno->{$seqno}--;
@@ -5204,10 +6195,12 @@ sub respace_tokens {
                     # of those was checked above. So we would only get here
                     # if the tokenizer has been changed to mark some other
                     # tokens with sequence numbers.
-                    my $type = $item->[_TYPE_];
-                    Fault(
+                    if (DEVEL_MODE) {
+                        my $type = $item->[_TYPE_];
+                        Fault(
 "Unexpected token type with sequence number: type='$type', seqno='$type_sequence'"
-                    );
+                        );
+                    }
                 }
             }
         }
@@ -5216,16 +6209,24 @@ sub respace_tokens {
         # or ignoring side comment lengths.
         my $token_length =
             $is_encoded_data
-          ? $length_function->( $item->[_TOKEN_] )
-          : length( $item->[_TOKEN_] );
+          ? $length_function->($token)
+          : length($token);
 
         # handle comments
         my $is_comment = $type eq '#';
         if ($is_comment) {
 
             # trim comments if necessary
-            if ( $item->[_TOKEN_] =~ s/\s+$// ) {
-                $token_length = $length_function->( $item->[_TOKEN_] );
+            my $ord = ord( substr( $token, -1, 1 ) );
+            if (
+                $ord > 0
+                && (   $ord < ORD_PRINTABLE_MIN
+                    || $ord > ORD_PRINTABLE_MAX )
+                && $token =~ s/\s+$//
+              )
+            {
+                $token_length = $length_function->($token);
+                $item->[_TOKEN_] = $token;
             }
 
             # Mark length of side comments as just 1 if sc lengths are ignored
@@ -5240,7 +6241,6 @@ sub respace_tokens {
             {
                 $set_permanently_broken->($seqno);
             }
-
         }
 
         $item->[_TOKEN_LENGTH_] = $token_length;
@@ -5252,11 +6252,18 @@ sub respace_tokens {
         $item->[_CUMULATIVE_LENGTH_] = $cumulative_length;
 
         if ( !$is_blank && !$is_comment ) {
-            $last_nonblank_type       = $type;
-            $last_nonblank_token      = $item->[_TOKEN_];
-            $last_nonblank_block_type = $item->[_BLOCK_TYPE_];
-            $last_nonblank_token_lx   = $item->[_LINE_INDEX_];
-            $nonblank_token_count++;
+
+            # Remember the most recent two non-blank, non-comment tokens.
+            # NOTE: the phantom semicolon code may change the output stack
+            # without updating these values.  Phantom semicolons are considered
+            # the same as blanks for now, but future needs might change that.
+            # See the related note in sub '$add_phantom_semicolon'.
+            $last_last_nonblank_code_type  = $last_nonblank_code_type;
+            $last_last_nonblank_code_token = $last_nonblank_code_token;
+
+            $last_nonblank_code_type  = $type;
+            $last_nonblank_code_token = $token;
+            $last_nonblank_block_type = $block_type;
 
             # count selected types
             if ( $is_counted_type{$type} ) {
@@ -5290,6 +6297,7 @@ sub respace_tokens {
 
         # and finally, add this item to the new array
         push @{$rLL_new}, $item;
+        return;
     };
 
     my $store_token_and_space = sub {
@@ -5303,15 +6311,18 @@ sub respace_tokens {
             && $rLL_new->[-1]->[_TYPE_] ne 'b'
             && $rOpts_add_whitespace )
         {
-            my $rcopy = copy_token_as_type( $item, 'b', ' ' );
+            my $rcopy = [ @{$item} ];
+            $rcopy->[_TYPE_]          = 'b';
+            $rcopy->[_TOKEN_]         = ' ';
+            $rcopy->[_TYPE_SEQUENCE_] = '';
+
             $rcopy->[_LINE_INDEX_] =
               $rLL_new->[-1]->[_LINE_INDEX_];
 
             # Patch 23-Jan-2021 to fix -lp blinkers:
             # The level and ci_level of newly created spaces should be the same
-            # as the previous token.  Otherwise the coding for the -lp option,
-            # in sub set_leading_whitespace, can create a blinking state in
-            # some rare cases.
+            # as the previous token.  Otherwise the coding for the -lp option
+            # can create a blinking state in some rare cases.
             $rcopy->[_LEVEL_] =
               $rLL_new->[-1]->[_LEVEL_];
             $rcopy->[_CI_LEVEL_] =
@@ -5322,23 +6333,7 @@ sub respace_tokens {
 
         # then the token
         $store_token->($item);
-    };
-
-    my $K_end_q = sub {
-        my ($KK) = @_;
-        my $K_end = $KK;
-
-        my $Kn = $KK + 1;
-        if ( $Kn <= $Kmax && $rLL->[$Kn]->[_TYPE_] eq 'b' ) { $Kn += 1 }
-
-        while ( $Kn <= $Kmax && $rLL->[$Kn]->[_TYPE_] eq 'q' ) {
-            $K_end = $Kn;
-
-            $Kn += 1;
-            if ( $Kn <= $Kmax && $rLL->[$Kn]->[_TYPE_] eq 'b' ) { $Kn += 1 }
-        }
-
-        return $K_end;
+        return;
     };
 
     my $add_phantom_semicolon = sub {
@@ -5349,34 +6344,36 @@ sub respace_tokens {
         return unless ( defined($Kp) );
 
         # we are only adding semicolons for certain block types
-        my $block_type = $rLL->[$KK]->[_BLOCK_TYPE_];
+        my $type_sequence = $rLL->[$KK]->[_TYPE_SEQUENCE_];
+        return unless ($type_sequence);
+        my $block_type = $rblock_type_of_seqno->{$type_sequence};
+        return unless ($block_type);
         return
           unless ( $ok_to_add_semicolon_for_block_type{$block_type}
             || $block_type =~ /^(sub|package)/
             || $block_type =~ /^\w+\:$/ );
 
-        my $type_sequence = $rLL->[$KK]->[_TYPE_SEQUENCE_];
-
-        my $previous_nonblank_type  = $rLL_new->[$Kp]->[_TYPE_];
-        my $previous_nonblank_token = $rLL_new->[$Kp]->[_TOKEN_];
+        my $type_p          = $rLL_new->[$Kp]->[_TYPE_];
+        my $token_p         = $rLL_new->[$Kp]->[_TOKEN_];
+        my $type_sequence_p = $rLL_new->[$Kp]->[_TYPE_SEQUENCE_];
 
         # Do not add a semicolon if...
         return
           if (
 
             # it would follow a comment (and be isolated)
-            $previous_nonblank_type eq '#'
+            $type_p eq '#'
 
             # it follows a code block ( because they are not always wanted
             # there and may add clutter)
-            || $rLL_new->[$Kp]->[_BLOCK_TYPE_]
+            || $type_sequence_p && $rblock_type_of_seqno->{$type_sequence_p}
 
             # it would follow a label
-            || $previous_nonblank_type eq 'J'
+            || $type_p eq 'J'
 
             # it would be inside a 'format' statement (and cause syntax error)
-            || (   $previous_nonblank_type eq 'k'
-                && $previous_nonblank_token =~ /format/ )
+            || (   $type_p eq 'k'
+                && $token_p =~ /format/ )
 
           );
 
@@ -5392,7 +6389,7 @@ sub respace_tokens {
         # If it is also a CLOSING token we have to look closer...
         if (
                $seqno_inner
-            && $is_closing_token{$previous_nonblank_token}
+            && $is_closing_token{$token_p}
 
             # we only need to look if there is just one inner container..
             && defined( $rchildren_of_seqno->{$type_sequence} )
@@ -5444,8 +6441,11 @@ sub respace_tokens {
             $rLL_new->[$Ktop]->[_TOKEN_]        = $tok;
             $rLL_new->[$Ktop]->[_TOKEN_LENGTH_] = $len_tok;
             $rLL_new->[$Ktop]->[_TYPE_]         = ';';
-            $rLL_new->[$Ktop]->[_SLEVEL_] =
-              $rLL->[$KK]->[_SLEVEL_];
+
+            # NOTE: we are changing the output stack without updating variables
+            # $last_nonblank_code_type, etc. Future needs might require that
+            # those variables be updated here.  For now, it seems ok to skip
+            # this.
 
             # Save list of new K indexes of phantom semicolons.
             # This will be needed if we want to undo them for iterations in
@@ -5457,12 +6457,29 @@ sub respace_tokens {
         }
         else {
 
-            # insert a new token
+            # Patch for issue c078: keep line indexes in order.  If the top
+            # token is a space that we are keeping (due to '-wls=';') then
+            # we have to check that old line indexes stay in order.
+            # In very rare
+            # instances in which side comments have been deleted and converted
+            # into blanks, we may have filtered down multiple blanks into just
+            # one. In that case the top blank may have a higher line number
+            # than the previous nonblank token. Although the line indexes of
+            # blanks are not really significant, we need to keep them in order
+            # in order to pass error checks.
+            if ( $rLL_new->[$Ktop]->[_TYPE_] eq 'b' ) {
+                my $old_top_ix = $rLL_new->[$Ktop]->[_LINE_INDEX_];
+                my $new_top_ix = $rLL_new->[$Kp]->[_LINE_INDEX_];
+                if ( $new_top_ix < $old_top_ix ) {
+                    $rLL_new->[$Ktop]->[_LINE_INDEX_] = $new_top_ix;
+                }
+            }
+
             my $rcopy = copy_token_as_type( $rLL_new->[$Kp], ';', '' );
-            $rcopy->[_SLEVEL_] = $rLL->[$KK]->[_SLEVEL_];
             $store_token->($rcopy);
             push @{$rK_phantom_semicolons}, @{$rLL_new} - 1;
         }
+        return;
     };
 
     my $check_Q = sub {
@@ -5474,9 +6491,20 @@ sub respace_tokens {
         my $token = $rLL->[$KK]->[_TOKEN_];
         $self->note_embedded_tab($line_number) if ( $token =~ "\t" );
 
+        # The remainder of this routine looks for something like
+        #        '$var = s/xxx/yyy/;'
+        # in case it should have been '$var =~ s/xxx/yyy/;'
+
+        # Start by looking for a token begining with one of: s y m / tr
+        return
+          unless ( $is_s_y_m_slash{ substr( $token, 0, 1 ) }
+            || substr( $token, 0, 2 ) eq 'tr' );
+
+        # ... and preceded by one of: = == !=
         my $Kp = $self->K_previous_nonblank( undef, $rLL_new );
         return unless ( defined($Kp) );
-        my $previous_nonblank_type  = $rLL_new->[$Kp]->[_TYPE_];
+        my $previous_nonblank_type = $rLL_new->[$Kp]->[_TYPE_];
+        return unless ( $is_unexpected_equals{$previous_nonblank_type} );
         my $previous_nonblank_token = $rLL_new->[$Kp]->[_TOKEN_];
 
         my $previous_nonblank_type_2  = 'b';
@@ -5497,11 +6525,10 @@ sub respace_tokens {
         my $token_0 = $rLL->[$Kfirst]->[_TOKEN_];
         my $type_0  = $rLL->[$Kfirst]->[_TYPE_];
 
-        # make note of something like '$var = s/xxx/yyy/;'
-        # in case it should have been '$var =~ s/xxx/yyy/;'
         if (
-               $token =~ /^(s|tr|y|m|\/)/
-            && $previous_nonblank_token =~ /^(=|==|!=)$/
+            ##$token =~ /^(s|tr|y|m|\/)/
+            ##&& $previous_nonblank_token =~ /^(=|==|!=)$/
+            1
 
             # preceded by simple scalar
             && $previous_nonblank_type_2 eq 'i'
@@ -5515,21 +6542,20 @@ sub respace_tokens {
             && !( $type_0 eq 'k' && $token_0 =~ /^(my|our|local)$/ )
           )
         {
-            my $guess = substr( $last_nonblank_token, 0, 1 ) . '~';
+            my $lno   = 1 + $rLL_new->[$Kp]->[_LINE_INDEX_];
+            my $guess = substr( $previous_nonblank_token, 0, 1 ) . '~';
             complain(
-"Note: be sure you want '$previous_nonblank_token' instead of '$guess' here\n"
+"Line $lno: Note: be sure you want '$previous_nonblank_token' instead of '$guess' here\n"
             );
         }
+        return;
     };
 
-    ############################################
+    #-------------------------------------------
     # Main loop to respace all lines of the file
-    ############################################
+    #-------------------------------------------
     my $last_K_out;
 
-    # Testing option to break qw.  Do not use; it can make a mess.
-    my $ALLOW_BREAK_MULTILINE_QW = 0;
-    my $in_multiline_qw;
     foreach my $line_of_tokens ( @{$rlines} ) {
 
         my $input_line_number = $line_of_tokens->{_line_number};
@@ -5552,7 +6578,10 @@ sub respace_tokens {
         # package the tokenized lines as it received them.  If we
         # get a fault here it has not output a continuous sequence
         # of K values.  Or a line of CODE may have been mismarked as
-        # something else.
+        # something else.  There is no good way to continue after such an
+        # error.
+        # FIXME: Calling Fault will produce zero output; it would be best to
+        # find a way to dump the input file.
         if ( defined($last_K_out) ) {
             if ( $Kfirst != $last_K_out + 1 ) {
                 Fault(
@@ -5613,10 +6642,14 @@ sub respace_tokens {
                 }
                 else {
 
-                    # This line was mis-marked by sub scan_comment
+                    # This line was mis-marked by sub scan_comment.  Catch in
+                    # DEVEL_MODE, otherwise try to repair and keep going.
                     Fault(
                         "Program bug. A hanging side comment has been mismarked"
-                    );
+                    ) if (DEVEL_MODE);
+
+                    $CODE_type = "";
+                    $line_of_tokens->{_code_type} = $CODE_type;
                 }
             }
 
@@ -5657,32 +6690,17 @@ sub respace_tokens {
         # if last line was normal CODE.
         # Patch for rt #125012: use K_previous_code rather than '_nonblank'
         # because comments may disappear.
-        my $type_next  = $rLL->[$Kfirst]->[_TYPE_];
-        my $token_next = $rLL->[$Kfirst]->[_TOKEN_];
-        my $Kp         = $self->K_previous_code( undef, $rLL_new );
-        if (   $last_line_type eq 'CODE'
-            && $type_next ne 'b'
-            && defined($Kp) )
-        {
-            my $token_p = $rLL_new->[$Kp]->[_TOKEN_];
-            my $type_p  = $rLL_new->[$Kp]->[_TYPE_];
-
-            my ( $token_pp, $type_pp );
-            my $Kpp = $self->K_previous_code( $Kp, $rLL_new );
-            if ( defined($Kpp) ) {
-                $token_pp = $rLL_new->[$Kpp]->[_TOKEN_];
-                $type_pp  = $rLL_new->[$Kpp]->[_TYPE_];
-            }
-            else {
-                $token_pp = ";";
-                $type_pp  = ';';
-            }
-
+        if ( $last_line_type eq 'CODE' ) {
+            my $type_next  = $rLL->[$Kfirst]->[_TYPE_];
+            my $token_next = $rLL->[$Kfirst]->[_TOKEN_];
             if (
-
                 is_essential_whitespace(
-                    $token_pp, $type_pp,    $token_p,
-                    $type_p,   $token_next, $type_next,
+                    $last_last_nonblank_code_token,
+                    $last_last_nonblank_code_type,
+                    $last_nonblank_code_token,
+                    $last_nonblank_code_type,
+                    $token_next,
+                    $type_next,
                 )
               )
             {
@@ -5705,9 +6723,9 @@ sub respace_tokens {
             }
         }
 
-        ########################################################
+        #-------------------------------------------------------
         # Loop to copy all tokens on this line, with any changes
-        ########################################################
+        #-------------------------------------------------------
         my $type_sequence;
         for ( my $KK = $Kfirst ; $KK <= $Klast ; $KK++ ) {
             $Ktoken_vars = $KK;
@@ -5737,30 +6755,19 @@ sub respace_tokens {
                     || $rOpts_delete_old_whitespace )
                 {
 
-                    my $Kp = $self->K_previous_nonblank($KK);
-                    next unless defined($Kp);
-                    my $token_p = $rLL->[$Kp]->[_TOKEN_];
-                    my $type_p  = $rLL->[$Kp]->[_TYPE_];
-
-                    my ( $token_pp, $type_pp );
-
-                    my $Kpp = $self->K_previous_nonblank($Kp);
-                    if ( defined($Kpp) ) {
-                        $token_pp = $rLL->[$Kpp]->[_TOKEN_];
-                        $type_pp  = $rLL->[$Kpp]->[_TYPE_];
-                    }
-                    else {
-                        $token_pp = ";";
-                        $type_pp  = ';';
-                    }
                     my $token_next = $rLL->[$Knext]->[_TOKEN_];
                     my $type_next  = $rLL->[$Knext]->[_TYPE_];
 
                     my $do_not_delete = is_essential_whitespace(
-                        $token_pp, $type_pp,    $token_p,
-                        $type_p,   $token_next, $type_next,
+                        $last_last_nonblank_code_token,
+                        $last_last_nonblank_code_type,
+                        $last_nonblank_code_token,
+                        $last_nonblank_code_type,
+                        $token_next,
+                        $type_next,
                     );
 
+                    # Note that repeated blanks will get filtered out here
                     next unless ($do_not_delete);
                 }
 
@@ -5774,54 +6781,62 @@ sub respace_tokens {
 
             if ($type_sequence) {
 
-                if ( $is_closing_token{$token} ) {
-
-                    # Insert a tentative missing semicolon if the next token is
-                    # a closing block brace
-                    if (
-                           $type eq '}'
-                        && $token eq '}'
+                # Insert a tentative missing semicolon if the next token is
+                # a closing block brace
+                if (
+                       $type eq '}'
+                    && $token eq '}'
 
-                        # not preceded by a ';'
-                        && $last_nonblank_type ne ';'
+                    # not preceded by a ';'
+                    && $last_nonblank_code_type ne ';'
 
-                        # and this is not a VERSION stmt (is all one line, we
-                        # are not inserting semicolons on one-line blocks)
-                        && $CODE_type ne 'VER'
+                    # and this is not a VERSION stmt (is all one line, we
+                    # are not inserting semicolons on one-line blocks)
+                    && $CODE_type ne 'VER'
 
-                        # and we are allowed to add semicolons
-                        && $rOpts->{'add-semicolons'}
-                      )
-                    {
-                        $add_phantom_semicolon->($KK);
-                    }
+                    # and we are allowed to add semicolons
+                    && $rOpts->{'add-semicolons'}
+                  )
+                {
+                    $add_phantom_semicolon->($KK);
                 }
             }
 
             # Modify certain tokens here for whitespace
             # The following is not yet done, but could be:
             #   sub (x x x)
-            elsif ( $type =~ /^[wit]$/ ) {
+            #     ( $type =~ /^[wit]$/ )
+            elsif ( $is_wit{$type} ) {
 
-                # Examples: <<snippets/space1.in>>
                 # change '$  var'  to '$var' etc
                 # change '@    '   to '@'
-                my ( $sigil, $word ) = split /\s+/, $token, 2;
-                if ( length($sigil) == 1
-                    && $sigil =~ /^[\$\&\%\*\@]$/ )
+                # Examples: <<snippets/space1.in>>
+                my $ord = ord( substr( $token, 1, 1 ) );
+                if (
+
+                    # quick test for possible blank at second char
+                    $ord > 0 && ( $ord < ORD_PRINTABLE_MIN
+                        || $ord > ORD_PRINTABLE_MAX )
+                  )
                 {
-                    $token = $sigil;
-                    $token .= $word if ($word);
-                    $rtoken_vars->[_TOKEN_] = $token;
+                    my ( $sigil, $word ) = split /\s+/, $token, 2;
+
+                    # $sigil =~ /^[\$\&\%\*\@]$/ )
+                    if ( $is_sigil{$sigil} ) {
+                        $token = $sigil;
+                        $token .= $word if ( defined($word) );    # fix c104
+                        $rtoken_vars->[_TOKEN_] = $token;
+                    }
                 }
 
-                # Split identifiers with leading arrows, inserting blanks if
-                # necessary.  It is easier and safer here than in the
-                # tokenizer.  For example '->new' becomes two tokens, '->' and
-                # 'new' with a possible blank between.
+                # Split identifiers with leading arrows, inserting blanks
+                # if necessary.  It is easier and safer here than in the
+                # tokenizer.  For example '->new' becomes two tokens, '->'
+                # and 'new' with a possible blank between.
                 #
                 # Note: there is a related patch in sub set_whitespace_flags
-                if (   substr( $token, 0, 1 ) eq '-'
+                elsif (length($token) > 2
+                    && substr( $token, 0, 2 ) eq '->'
                     && $token =~ /^\-\>(.*)$/
                     && $1 )
                 {
@@ -5864,40 +6879,60 @@ sub respace_tokens {
                     next;
                 }
 
-                if ( $token =~ /$ANYSUB_PATTERN/ ) {
+                # Trim certain spaces in identifiers
+                if ( $type eq 'i' ) {
+
+                    if (
+                        (
+                            substr( $token, 0, 3 ) eq 'sub'
+                            || $rOpts_sub_alias_list
+                        )
+                        && $token =~ /$SUB_PATTERN/
+                      )
+                    {
+
+                        # -spp = 0 : no space before opening prototype paren
+                        # -spp = 1 : stable (follow input spacing)
+                        # -spp = 2 : always space before opening prototype paren
+                        my $spp = $rOpts->{'space-prototype-paren'};
+                        if ( defined($spp) ) {
+                            if    ( $spp == 0 ) { $token =~ s/\s+\(/\(/; }
+                            elsif ( $spp == 2 ) { $token =~ s/\(/ (/; }
+                        }
 
-                    # -spp = 0 : no space before opening prototype paren
-                    # -spp = 1 : stable (follow input spacing)
-                    # -spp = 2 : always space before opening prototype paren
-                    my $spp = $rOpts->{'space-prototype-paren'};
-                    if ( defined($spp) ) {
-                        if    ( $spp == 0 ) { $token =~ s/\s+\(/\(/; }
-                        elsif ( $spp == 2 ) { $token =~ s/\(/ (/; }
+                        # one space max, and no tabs
+                        $token =~ s/\s+/ /g;
+                        $rtoken_vars->[_TOKEN_] = $token;
                     }
 
-                    # one space max, and no tabs
-                    $token =~ s/\s+/ /g;
-                    $rtoken_vars->[_TOKEN_] = $token;
-                }
+                    # clean up spaces in package identifiers, like
+                    #   "package        Bob::Dog;"
+                    elsif ( substr( $token, 0, 7 ) eq 'package'
+                        && $token =~ /^package\s/ )
+                    {
+                        $token =~ s/\s+/ /g;
+                        $rtoken_vars->[_TOKEN_] = $token;
+                    }
 
-                # clean up spaces in package identifiers, like
-                #   "package        Bob::Dog;"
-                if ( $token =~ /^package\s/ ) {
-                    $token =~ s/\s+/ /g;
-                    $rtoken_vars->[_TOKEN_] = $token;
-                }
+                    # trim identifiers of trailing blanks which can occur
+                    # under some unusual circumstances, such as if the
+                    # identifier 'witch' has trailing blanks on input here:
+                    #
+                    # sub
+                    # witch
+                    # ()   # prototype may be on new line ...
+                    # ...
+                    my $ord = ord( substr( $token, -1, 1 ) );
+                    if (
 
-                # trim identifiers of trailing blanks which can occur
-                # under some unusual circumstances, such as if the
-                # identifier 'witch' has trailing blanks on input here:
-                #
-                # sub
-                # witch
-                # ()   # prototype may be on new line ...
-                # ...
-                if ( $type eq 'i' ) {
-                    $token =~ s/\s+$//g;
-                    $rtoken_vars->[_TOKEN_] = $token;
+                        # quick check for possible ending space
+                        $ord > 0 && ( $ord < ORD_PRINTABLE_MIN
+                            || $ord > ORD_PRINTABLE_MAX )
+                      )
+                    {
+                        $token =~ s/\s+$//g;
+                        $rtoken_vars->[_TOKEN_] = $token;
+                    }
                 }
             }
 
@@ -5911,7 +6946,8 @@ sub respace_tokens {
                     $rOpts->{'delete-semicolons'}
                     && (
                         (
-                            $last_nonblank_type eq '}'
+                               $last_nonblank_block_type
+                            && $last_nonblank_code_type eq '}'
                             && (
                                 $is_block_without_semicolon{
                                     $last_nonblank_block_type}
@@ -5919,14 +6955,14 @@ sub respace_tokens {
                                 || $last_nonblank_block_type =~ /^\w+:$/
                             )
                         )
-                        || $last_nonblank_type eq ';'
+                        || $last_nonblank_code_type eq ';'
                     )
                   )
                 {
 
                     # This looks like a deletable semicolon, but even if a
-                    # semicolon can be deleted it is necessarily best to do so.
-                    # We apply these additional rules for deletion:
+                    # semicolon can be deleted it is not necessarily best to do
+                    # so.  We apply these additional rules for deletion:
                     # - Always ok to delete a ';' at the end of a line
                     # - Never delete a ';' before a '#' because it would
                     #   promote it to a block comment.
@@ -5950,8 +6986,9 @@ sub respace_tokens {
 
                     # do not delete only nonblank token in a file
                     else {
+                        my $Kp = $self->K_previous_code( undef, $rLL_new );
                         my $Kn = $self->K_next_nonblank($KK);
-                        $ok_to_delete = defined($Kn) || $nonblank_token_count;
+                        $ok_to_delete = defined($Kn) || defined($Kp);
                     }
 
                     if ($ok_to_delete) {
@@ -5964,12 +7001,18 @@ sub respace_tokens {
                 }
             }
 
-            # patch to add space to something like "x10"
-            # This avoids having to split this token in the pre-tokenizer
+            # Old patch to add space to something like "x10".
+            # Note: This is now done in the Tokenizer, but this code remains
+            # for reference.
             elsif ( $type eq 'n' ) {
-                if ( $token =~ /^x\d+/ ) {
+                if ( substr( $token, 0, 1 ) eq 'x' && $token =~ /^x\d+/ ) {
                     $token =~ s/x/x /;
                     $rtoken_vars->[_TOKEN_] = $token;
+                    if (DEVEL_MODE) {
+                        Fault(<<EOM);
+Near line $input_line_number, Unexpected need to split a token '$token' - this should now be done by the Tokenizer
+EOM
+                    }
                 }
             }
 
@@ -5983,82 +7026,10 @@ sub respace_tokens {
                 $rtoken_vars->[_TOKEN_] = $token;
                 $self->note_embedded_tab($input_line_number)
                   if ( $token =~ "\t" );
-
-                if ($in_multiline_qw) {
-
-                    # If we are at the end of a multiline qw ..
-                    if ( $in_multiline_qw == $KK ) {
-
-                 # Split off the closing delimiter character
-                 # so that the formatter can put a line break there if necessary
-                        my $part1 = $token;
-                        my $part2 = substr( $part1, -1, 1, "" );
-
-                        if ($part1) {
-                            my $rcopy =
-                              copy_token_as_type( $rtoken_vars, 'q', $part1 );
-                            $store_token->($rcopy);
-                            $token = $part2;
-                            $rtoken_vars->[_TOKEN_] = $token;
-
-                        }
-                        $in_multiline_qw = undef;
-
-                        # store without preceding blank
-                        $store_token->($rtoken_vars);
-                        next;
-                    }
-                    else {
-                        # continuing a multiline qw
-                        $store_token->($rtoken_vars);
-                        next;
-                    }
-                }
-
-                else {
-
-                    # we are encountered new qw token...see if multiline
-                    if ($ALLOW_BREAK_MULTILINE_QW) {
-                        my $K_end = $K_end_q->($KK);
-                        if ( $K_end != $KK ) {
-
-                            # Starting multiline qw...
-                            # set flag equal to the ending K
-                            $in_multiline_qw = $K_end;
-
-                          # Split off the leading part so that the formatter can
-                          # put a line break there if necessary
-                            if ( $token =~ /^(qw\s*.)(.*)$/ ) {
-                                my $part1 = $1;
-                                my $part2 = $2;
-                                if ($part2) {
-                                    my $rcopy =
-                                      copy_token_as_type( $rtoken_vars, 'q',
-                                        $part1 );
-                                    $store_token_and_space->(
-                                        $rcopy,
-                                        $rwhitespace_flags->[$KK] == WS_YES
-                                    );
-                                    $token = $part2;
-                                    $rtoken_vars->[_TOKEN_] = $token;
-
-                                   # Second part goes without intermediate blank
-                                    $store_token->($rtoken_vars);
-                                    next;
-                                }
-                            }
-                        }
-                    }
-                    else {
-
-                        # this is a new single token qw -
-                        # store with possible preceding blank
-                        $store_token_and_space->(
-                            $rtoken_vars, $rwhitespace_flags->[$KK] == WS_YES
-                        );
-                        next;
-                    }
-                }
+                $store_token_and_space->(
+                    $rtoken_vars, $rwhitespace_flags->[$KK] == WS_YES
+                );
+                next;
             } ## end if ( $type eq 'q' )
 
             # change 'LABEL   :'   to 'LABEL:'
@@ -6144,7 +7115,7 @@ sub respace_tokens {
         # type here now that we have had a better look at the contents of the
         # container. This fixes case b1085. To find the corresponding code in
         # Tokenizer.pm search for 'b1085' with an editor.
-        my $block_type = $rLL_new->[$K_opening]->[_BLOCK_TYPE_];
+        my $block_type = $rblock_type_of_seqno->{$seqno};
         if ( $block_type && substr( $block_type, -1, 1 ) eq ' ' ) {
 
             # Always remove the trailing space
@@ -6172,8 +7143,7 @@ sub respace_tokens {
                 $rLL_new->[$K_closing]->[_CI_LEVEL_] = 1;
             }
 
-            $rLL_new->[$K_opening]->[_BLOCK_TYPE_] = $block_type;
-            $rLL_new->[$K_closing]->[_BLOCK_TYPE_] = $block_type;
+            $rblock_type_of_seqno->{$seqno} = $block_type;
         }
 
         # Handle a list container
@@ -6281,8 +7251,7 @@ sub respace_tokens {
     if ( @{$rLL_new} ) { $Klimit = @{$rLL_new} - 1 }
     $self->[_Klimit_] = $Klimit;
 
-    # DEBUG OPTION: make sure the new array looks okay.
-    # This is no longer needed but should be retained for future development.
+    # During development, verify that the new array still looks okay.
     DEVEL_MODE && $self->check_token_array();
 
     # reset the token limits of each line
@@ -6310,16 +7279,22 @@ sub copy_token_as_type {
     }
     else {
 
-        # This sub assumes it will be called with just two types, 'b' or 'q'
-        Fault(
-"Programming error: copy_token_as has type $type but should be 'b' or 'q'"
-        );
+        # Unexpected type ... this sub will work as long as both $token and
+        # $type are defined, but we should catch any unexpected types during
+        # development.
+        if (DEVEL_MODE) {
+            Fault(<<EOM);
+sub 'copy_token_as_type' received token type '$type' but expects just one of: 'b' 'q' '->' or ';'
+EOM
+        }
+        else {
+            # shouldn't happen
+        }
     }
 
     my @rnew_token = @{$rold_token};
     $rnew_token[_TYPE_]          = $type;
     $rnew_token[_TOKEN_]         = $token;
-    $rnew_token[_BLOCK_TYPE_]    = '';
     $rnew_token[_TYPE_SEQUENCE_] = '';
     return \@rnew_token;
 }
@@ -6358,7 +7333,8 @@ sub K_next_code {
             # We seem to have encountered a gap in our array.
             # This shouldn't happen because sub write_line() pushed
             # items into the $rLL array.
-            Fault("Undefined entry for k=$Knnb");
+            Fault("Undefined entry for k=$Knnb") if (DEVEL_MODE);
+            return;
         }
         if (   $rLL->[$Knnb]->[_TYPE_] ne 'b'
             && $rLL->[$Knnb]->[_TYPE_] ne '#' )
@@ -6400,7 +7376,8 @@ sub K_next_nonblank {
         # $store_token() within sub respace_tokens.  Tokens are pushed on
         # so there shouldn't be any gaps.
         if ( !defined( $rLL->[$Knnb] ) ) {
-            Fault("Undefined entry for k=$Knnb");
+            Fault("Undefined entry for k=$Knnb") if (DEVEL_MODE);
+            return;
         }
         if ( $rLL->[$Knnb]->[_TYPE_] ne 'b' ) { return $Knnb }
         $Knnb++;
@@ -6425,7 +7402,8 @@ sub K_previous_code {
         # avoid this error.
         Fault(
 "Program Bug: K_previous_nonblank_new called with K=$KK which exceeds $Num"
-        );
+        ) if (DEVEL_MODE);
+        return;
     }
     my $Kpnb = $KK - 1;
     while ( $Kpnb >= 0 ) {
@@ -6456,7 +7434,8 @@ sub K_previous_nonblank {
         # avoid this error.
         Fault(
 "Program Bug: K_previous_nonblank_new called with K=$KK which exceeds $Num"
-        );
+        ) if (DEVEL_MODE);
+        return;
     }
     my $Kpnb = $KK - 1;
     return unless ( $Kpnb >= 0 );
@@ -6474,39 +7453,15 @@ sub K_previous_nonblank {
     return;
 }
 
-sub get_old_line_index {
-
-    # return index of the original line that token K was on
-    my ( $self, $K ) = @_;
-    my $rLL = $self->[_rLL_];
-    return 0 unless defined($K);
-    return $rLL->[$K]->[_LINE_INDEX_];
-}
-
-sub get_old_line_count {
-
-    # return number of input lines separating two tokens
-    my ( $self, $Kbeg, $Kend ) = @_;
-    my $rLL = $self->[_rLL_];
-    return 0 unless defined($Kbeg);
-    return 0 unless defined($Kend);
-    return $rLL->[$Kend]->[_LINE_INDEX_] - $rLL->[$Kbeg]->[_LINE_INDEX_] + 1;
-}
-
 sub parent_seqno_by_K {
 
     # Return the sequence number of the parent container of token K, if any.
 
     my ( $self, $KK ) = @_;
-    return unless defined($KK);
-
-    # Note: This routine is relatively slow. I tried replacing it with a hash
-    # which is easily created in sub respace_tokens. But the total time with a
-    # hash was greater because this routine is called once per line whereas a
-    # hash must be created token-by-token.
+    my $rLL = $self->[_rLL_];
 
-    my $rLL   = $self->[_rLL_];
-    my $KNEXT = $KK;
+    # The task is to jump forward to the next container token
+    # and use the sequence number of either it or its parent.
 
     # For example, consider the following with seqno=5 of the '[' and ']'
     # being called with index K of the first token of each line:
@@ -6523,34 +7478,28 @@ sub parent_seqno_by_K {
     # unbalanced files, last sequence number will either be undefined or it may
     # be at a deeper level.  In either case we will just return SEQ_ROOT to
     # have a defined value and allow formatting to proceed.
-    my $parent_seqno = SEQ_ROOT;
-    while ( defined($KNEXT) ) {
-        my $Kt = $KNEXT;
-        $KNEXT = $rLL->[$KNEXT]->[_KNEXT_SEQ_ITEM_];
-        my $rtoken_vars   = $rLL->[$Kt];
-        my $type          = $rtoken_vars->[_TYPE_];
-        my $type_sequence = $rtoken_vars->[_TYPE_SEQUENCE_];
+    my $parent_seqno  = SEQ_ROOT;
+    my $type_sequence = $rLL->[$KK]->[_TYPE_SEQUENCE_];
+    if ($type_sequence) {
+        $parent_seqno = $self->[_rparent_of_seqno_]->{$type_sequence};
+    }
+    else {
+        my $Kt = $rLL->[$KK]->[_KNEXT_SEQ_ITEM_];
+        if ( defined($Kt) ) {
+            $type_sequence = $rLL->[$Kt]->[_TYPE_SEQUENCE_];
+            my $type = $rLL->[$Kt]->[_TYPE_];
 
-        # if next container token is closing, it is the parent seqno
-        if ( $is_closing_type{$type} ) {
-            if ( $Kt > $KK ) {
+            # if next container token is closing, it is the parent seqno
+            if ( $is_closing_type{$type} ) {
                 $parent_seqno = $type_sequence;
             }
+
+            # otherwise we want its parent container
             else {
                 $parent_seqno = $self->[_rparent_of_seqno_]->{$type_sequence};
             }
-            last;
-        }
-
-        # if next container token is opening, we want its parent container
-        elsif ( $is_opening_type{$type} ) {
-            $parent_seqno = $self->[_rparent_of_seqno_]->{$type_sequence};
-            last;
         }
-
-        # not a container - must be ternary - keep going
     }
-
     $parent_seqno = SEQ_ROOT unless ( defined($parent_seqno) );
     return $parent_seqno;
 }
@@ -6563,12 +7512,10 @@ sub is_in_block_by_i {
     #     or is at root level
     #     or there is some kind of error (i.e. unbalanced file)
     # returns false otherwise
+    return 1 if ( $i < 0 );    # shouldn't happen, bad call
     my $seqno = $parent_seqno_to_go[$i];
     return 1 if ( !$seqno || $seqno eq SEQ_ROOT );
-    my $Kopening = $self->[_K_opening_container_]->{$seqno};
-    return 1 unless defined($Kopening);
-    my $rLL = $self->[_rLL_];
-    return 1 if $rLL->[$Kopening]->[_BLOCK_TYPE_];
+    return 1 if ( $self->[_rblock_type_of_seqno_]->{$seqno} );
     return;
 }
 
@@ -6617,58 +7564,88 @@ sub resync_lines_and_tokens {
     # since they have probably changed due to inserting and deleting blanks
     # and a few other tokens.
 
-    my $Kmax = -1;
-
     # This is the next token and its line index:
     my $Knext = 0;
-    my $inext;
-    if ( defined($rLL) && @{$rLL} ) {
-        $Kmax  = @{$rLL} - 1;
-        $inext = $rLL->[$Knext]->[_LINE_INDEX_];
+    my $Kmax  = defined($Klimit) ? $Klimit : -1;
+
+    # Verify that old line indexes are in still order.  If this error occurs,
+    # check locations where sub 'respace_tokens' creates new tokens (like
+    # blank spaces).  It must have set a bad old line index.
+    if ( DEVEL_MODE && defined($Klimit) ) {
+        my $iline = $rLL->[0]->[_LINE_INDEX_];
+        for ( my $KK = 1 ; $KK <= $Klimit ; $KK++ ) {
+            my $iline_last = $iline;
+            $iline = $rLL->[$KK]->[_LINE_INDEX_];
+            if ( $iline < $iline_last ) {
+                my $KK_m    = $KK - 1;
+                my $token_m = $rLL->[$KK_m]->[_TOKEN_];
+                my $token   = $rLL->[$KK]->[_TOKEN_];
+                my $type_m  = $rLL->[$KK_m]->[_TYPE_];
+                my $type    = $rLL->[$KK]->[_TYPE_];
+                Fault(<<EOM);
+Line indexes out of order at index K=$KK:
+at KK-1 =$KK_m: old line=$iline_last, type='$type_m', token='$token_m'
+at KK   =$KK: old line=$iline, type='$type', token='$token',
+EOM
+            }
+        }
     }
 
-    # Remember the most recently output token index
-    my $Klast_out;
-
     my $iline = -1;
     foreach my $line_of_tokens ( @{$rlines} ) {
         $iline++;
         my $line_type = $line_of_tokens->{_line_type};
-        my $CODE_type = $line_of_tokens->{_code_type};
         if ( $line_type eq 'CODE' ) {
 
-            my @K_array;
-            my $rK_range;
-            if ( $Knext <= $Kmax ) {
-                $inext = $rLL->[$Knext]->[_LINE_INDEX_];
-                while ( $inext <= $iline ) {
-                    push @K_array, $Knext;
-                    $Knext += 1;
-                    if ( $Knext > $Kmax ) {
-                        $inext = undef;
-                        last;
-                    }
-                    $inext = $rLL->[$Knext]->[_LINE_INDEX_];
-                }
-            }
-
-            # Delete any terminal blank token
-            if (@K_array) {
-                if ( $rLL->[ $K_array[-1] ]->[_TYPE_] eq 'b' ) {
-                    pop @K_array;
-                }
+            # Get the old number of tokens on this line
+            my $rK_range_old = $line_of_tokens->{_rK_range};
+            my ( $Kfirst_old, $Klast_old ) = @{$rK_range_old};
+            my $Kdiff_old = 0;
+            if ( defined($Kfirst_old) ) {
+                $Kdiff_old = $Klast_old - $Kfirst_old;
             }
 
-            # Define the range of K indexes for the line:
+            # Find the range of NEW K indexes for the line:
             # $Kfirst = index of first token on line
-            # $Klast_out = index of last token on line
+            # $Klast  = index of last token on line
             my ( $Kfirst, $Klast );
-            if (@K_array) {
-                $Kfirst    = $K_array[0];
-                $Klast     = $K_array[-1];
-                $Klast_out = $Klast;
 
-                if ( defined($Kfirst) ) {
+            my $Knext_beg = $Knext;    # this will be $Kfirst if we find tokens
+
+            # Optimization: Although the actual K indexes may be completely
+            # changed after respacing, the number of tokens on any given line
+            # will often be nearly unchanged.  So we will see if we can start
+            # our search by guessing that the new line has the same number
+            # of tokens as the old line.
+            my $Knext_guess = $Knext + $Kdiff_old;
+            if (   $Knext_guess > $Knext
+                && $Knext_guess < $Kmax
+                && $rLL->[$Knext_guess]->[_LINE_INDEX_] <= $iline )
+            {
+
+                # the guess is good, so we can start our search here
+                $Knext = $Knext_guess + 1;
+            }
+
+            while ($Knext <= $Kmax
+                && $rLL->[$Knext]->[_LINE_INDEX_] <= $iline )
+            {
+                $Knext++;
+            }
+
+            if ( $Knext > $Knext_beg ) {
+
+                $Klast = $Knext - 1;
+
+                # Delete any terminal blank token
+                if ( $rLL->[$Klast]->[_TYPE_] eq 'b' ) { $Klast -= 1 }
+
+                if ( $Klast < $Knext_beg ) {
+                    $Klast = undef;
+                }
+                else {
+
+                    $Kfirst = $Knext_beg;
 
                     # Save ranges of non-comment code. This will be used by
                     # sub keep_old_line_breaks.
@@ -6678,7 +7655,8 @@ sub resync_lines_and_tokens {
 
                     # Only save ending K indexes of code types which are blank
                     # or 'VER'.  These will be used for a convergence check.
-                    # See related code in sub 'send_lines_to_vertical_aligner'.
+                    # See related code in sub 'convey_batch_to_vertical_aligner'
+                    my $CODE_type = $line_of_tokens->{_code_type};
                     if (  !$CODE_type
                         || $CODE_type eq 'VER' )
                     {
@@ -6698,8 +7676,8 @@ sub resync_lines_and_tokens {
             # Deleting semicolons can create new empty code lines
             # which should be marked as blank
             if ( !defined($Kfirst) ) {
-                my $code_type = $line_of_tokens->{_code_type};
-                if ( !$code_type ) {
+                my $CODE_type = $line_of_tokens->{_code_type};
+                if ( !$CODE_type ) {
                     $line_of_tokens->{_code_type} = 'BL';
                 }
             }
@@ -6709,7 +7687,10 @@ sub resync_lines_and_tokens {
     # There shouldn't be any nodes beyond the last one.  This routine is
     # relinking lines and tokens after the tokens have been respaced.  A fault
     # here indicates some kind of bug has been introduced into the above loops.
-    if ( defined($inext) ) {
+    # There is not good way to keep going; we better stop here.
+    # FIXME: This will produce zero output. it would be best to find a way to
+    # dump the input file.
+    if ( $Knext <= $Kmax ) {
 
         Fault("unexpected tokens at end of file when reconstructing lines");
     }
@@ -6750,7 +7731,6 @@ sub resync_lines_and_tokens {
             $ris_essential_old_breakpoint->{$Klast_prev} = 1;
         }
     }
-
     return;
 }
 
@@ -6778,7 +7758,7 @@ sub keep_old_line_breaks {
     my $ris_broken_container = $self->[_ris_broken_container_];
     my $ris_list_by_seqno    = $self->[_ris_list_by_seqno_];
 
-    # This code moved here from sub scan_list to fix b1120
+    # This code moved here from sub break_lists to fix b1120
     if ( $rOpts->{'break-at-old-method-breakpoints'} ) {
         foreach my $item ( @{$rKrange_code_without_comments} ) {
             my ( $Kfirst, $Klast ) = @{$item};
@@ -6803,16 +7783,17 @@ sub keep_old_line_breaks {
                 my $seqno = $rLL->[$Kfirst]->[_TYPE_SEQUENCE_];
                 next unless ($seqno);
 
-                # Patch to avoid blinkers: but do not do this unless the
-                # container holds a list, or the opening and closing parens are
-                # separated by more than one line.
-                # Fixes case b977.
-                next
-                  if (
-                    !$ris_list_by_seqno->{$seqno}
-                    && (  !$ris_broken_container->{$seqno}
-                        || $ris_broken_container->{$seqno} <= 1 )
-                  );
+                # Note: in previous versions there was a fix here to avoid
+                # instability between conflicting -bom and -pvt or -pvtc flags.
+                # The fix skipped -bom for a small line difference.  But this
+                # was troublesome, and instead the fix has been moved to
+                # sub set_vertical_tightness_flags where priority is given to
+                # the -bom flag over -pvt and -pvtc flags.  Both opening and
+                # closing paren flags are involved because even though -bom only
+                # requests breaking before the closing paren, automated logic
+                # opens the opening paren when the closing paren opens.
+                # Relevant cases are b977, b1215, b1270, b1303
+
                 $rwant_container_open->{$seqno} = 1;
             }
         }
@@ -6820,18 +7801,58 @@ sub keep_old_line_breaks {
 
     return unless ( %keep_break_before_type || %keep_break_after_type );
 
-    foreach my $item ( @{$rKrange_code_without_comments} ) {
-        my ( $Kfirst, $Klast ) = @{$item};
+    my $check_for_break = sub {
+        my ( $KK, $rkeep_break_hash, $rbreak_hash ) = @_;
+        my $seqno = $rLL->[$KK]->[_TYPE_SEQUENCE_];
 
-        my $type_first = $rLL->[$Kfirst]->[_TYPE_];
-        if ( $keep_break_before_type{$type_first} ) {
-            $rbreak_before_Kfirst->{$Kfirst} = 1;
+        # non-container tokens use the type as the key
+        if ( !$seqno ) {
+            my $type = $rLL->[$KK]->[_TYPE_];
+            if ( $rkeep_break_hash->{$type} ) {
+                $rbreak_hash->{$KK} = 1;
+            }
         }
 
-        my $type_last = $rLL->[$Klast]->[_TYPE_];
-        if ( $keep_break_after_type{$type_last} ) {
-            $rbreak_after_Klast->{$Klast} = 1;
+        # container tokens use the token as the key
+        else {
+            my $token = $rLL->[$KK]->[_TOKEN_];
+            my $flag  = $rkeep_break_hash->{$token};
+            if ($flag) {
+
+                my $match = $flag eq '1' || $flag eq '*';
+
+                # check for special matching codes
+                if ( !$match ) {
+                    if ( $token eq '(' || $token eq ')' ) {
+                        $match = $self->match_paren_flag( $KK, $flag );
+                    }
+                    elsif ( $token eq '{' || $token eq '}' ) {
+
+                        # These tentative codes 'b' and 'B' for brace types are
+                        # placeholders for possible future brace types. They
+                        # are not documented and may be changed.
+                        my $block_type =
+                          $self->[_rblock_type_of_seqno_]->{$seqno};
+                        if    ( $flag eq 'b' ) { $match = $block_type }
+                        elsif ( $flag eq 'B' ) { $match = !$block_type }
+                        else {
+                            # unknown code - no match
+                        }
+                    }
+                }
+                $rbreak_hash->{$KK} = 1 if ($match);
+            }
         }
+    };
+
+    foreach my $item ( @{$rKrange_code_without_comments} ) {
+        my ( $Kfirst, $Klast ) = @{$item};
+        $check_for_break->(
+            $Kfirst, \%keep_break_before_type, $rbreak_before_Kfirst
+        );
+        $check_for_break->(
+            $Klast, \%keep_break_after_type, $rbreak_after_Klast
+        );
     }
     return;
 }
@@ -6875,9 +7896,9 @@ sub weld_containers {
         $self->weld_nested_quotes();
     }
 
-    ##############################################################
+    #-------------------------------------------------------------
     # All welding is done. Finish setting up weld data structures.
-    ##############################################################
+    #-------------------------------------------------------------
 
     my $rLL                  = $self->[_rLL_];
     my $rK_weld_left         = $self->[_rK_weld_left_];
@@ -6888,21 +7909,29 @@ sub weld_containers {
     my @keys = keys %{$rK_weld_right};
     $total_weld_count = @keys;
 
-    # Note that this loop is processed in unsorted order for efficiency
+    # First pass to process binary welds.
+    # This loop is processed in unsorted order for efficiency.
     foreach my $Kstart (@keys) {
         my $Kend = $rK_weld_right->{$Kstart};
 
         # An error here would be due to an incorrect initialization introduced
         # in one of the above weld routines, like sub weld_nested.
         if ( $Kend <= $Kstart ) {
-            Fault("Bad weld link: Kend=$Kend <= Kstart=$Kstart\n");
+            Fault("Bad weld link: Kend=$Kend <= Kstart=$Kstart\n")
+              if (DEVEL_MODE);
+            next;
         }
 
-        $rweld_len_right_at_K->{$Kstart} =
-          $rLL->[$Kend]->[_CUMULATIVE_LENGTH_] -
-          $rLL->[$Kstart]->[_CUMULATIVE_LENGTH_];
-
-        $rK_weld_left->{$Kend} = $Kstart;    # fix in case of missing left link
+        # Set weld values for all tokens this welded pair
+        foreach ( $Kstart + 1 .. $Kend ) {
+            $rK_weld_left->{$_} = $Kstart;
+        }
+        foreach my $Kx ( $Kstart .. $Kend - 1 ) {
+            $rK_weld_right->{$Kx} = $Kend;
+            $rweld_len_right_at_K->{$Kx} =
+              $rLL->[$Kend]->[_CUMULATIVE_LENGTH_] -
+              $rLL->[$Kx]->[_CUMULATIVE_LENGTH_];
+        }
 
         # Remember the leftmost index of welds which continue to the right
         if ( defined( $rK_weld_right->{$Kend} )
@@ -6912,43 +7941,33 @@ sub weld_containers {
         }
     }
 
-    # Update the end index and lengths of any long welds to extend to the far
-    # end.  This has to be processed in sorted order.
-    # Left links added for b1173.
-    my $Kend = -1;
-    foreach my $Kstart ( sort { $a <=> $b } @K_multi_weld ) {
+    # Second pass to process chains of welds (these are rare).
+    # This has to be processed in sorted order.
+    if (@K_multi_weld) {
+        my $Kend = -1;
+        foreach my $Kstart ( sort { $a <=> $b } @K_multi_weld ) {
 
-        # skip any interior K which was originally missing a left link
-        next if ( $Kstart <= $Kend );
+            # Skip any interior K which was originally missing a left link
+            next if ( $Kstart <= $Kend );
 
-        my @Klist;
-        push @Klist, $Kstart;
-        $Kend = $rK_weld_right->{$Kstart};
-        $rK_weld_left->{$Kend} = $Kstart;
-        my $Knext = $rK_weld_right->{$Kend};
-        while ( defined($Knext) ) {
-            push @Klist, $Kend;
-            $Kend                  = $Knext;
-            $rK_weld_left->{$Kend} = $Kstart;
-            $Knext                 = $rK_weld_right->{$Kend};
-        }
-        pop @Klist;    #  values for last entry are already correct
-        foreach my $KK (@Klist) {
-
-            # Ending indexes must only be shifted to the right for long welds.
-            # An error here would be due to a programming error introduced in
-            # the code immediately above.
-            my $Kend_old = $rK_weld_right->{$KK};
-            if ( !defined($Kend_old) || $Kend < $Kend_old ) {
-                Fault(
-"Bad weld link at K=$KK, old end is K=$Kend_old, new end is $Kend\n"
-                );
+            # Find the end of this chain
+            $Kend = $rK_weld_right->{$Kstart};
+            my $Knext = $rK_weld_right->{$Kend};
+            while ( defined($Knext) ) {
+                $Kend  = $Knext;
+                $Knext = $rK_weld_right->{$Kend};
             }
 
-            $rK_weld_right->{$KK} = $Kend;
-            $rweld_len_right_at_K->{$KK} =
-              $rLL->[$Kend]->[_CUMULATIVE_LENGTH_] -
-              $rLL->[$KK]->[_CUMULATIVE_LENGTH_];
+            # Set weld values this chain
+            foreach ( $Kstart + 1 .. $Kend ) {
+                $rK_weld_left->{$_} = $Kstart;
+            }
+            foreach my $Kx ( $Kstart .. $Kend - 1 ) {
+                $rK_weld_right->{$Kx} = $Kend;
+                $rweld_len_right_at_K->{$Kx} =
+                  $rLL->[$Kend]->[_CUMULATIVE_LENGTH_] -
+                  $rLL->[$Kx]->[_CUMULATIVE_LENGTH_];
+            }
         }
     }
 
@@ -6966,8 +7985,9 @@ sub weld_cuddled_blocks {
 
     # Called once per file to handle cuddled formatting
 
-    my $rK_weld_left  = $self->[_rK_weld_left_];
-    my $rK_weld_right = $self->[_rK_weld_right_];
+    my $rK_weld_left         = $self->[_rK_weld_left_];
+    my $rK_weld_right        = $self->[_rK_weld_right_];
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
 
     # This routine implements the -cb flag by finding the appropriate
     # closing and opening block braces and welding them together.
@@ -7029,7 +8049,9 @@ sub weld_cuddled_blocks {
             # the bottom of sub 'respace_tokens' which set the values of
             # _KNEXT_SEQ_ITEM_.  Or an error has been introduced in the
             # loop control lines above.
-            Fault("sequence = $type_sequence not defined at K=$KK");
+            Fault("sequence = $type_sequence not defined at K=$KK")
+              if (DEVEL_MODE);
+            next;
         }
 
         # NOTE: we must use the original levels here. They can get changed
@@ -7048,7 +8070,7 @@ sub weld_cuddled_blocks {
 
         if ( $token eq '{' ) {
 
-            my $block_type = $rtoken_vars->[_BLOCK_TYPE_];
+            my $block_type = $rblock_type_of_seqno->{$type_sequence};
             if ( !$block_type ) {
 
                 # patch for unrecognized block types which may not be labeled
@@ -7058,7 +8080,6 @@ sub weld_cuddled_blocks {
                 }
                 next unless $Kp;
                 $block_type = $rLL->[$Kp]->[_TOKEN_];
-
             }
             if ( $in_chain{$level} ) {
 
@@ -7149,8 +8170,9 @@ sub find_nested_pairs {
     return unless ( defined($rLL) && @{$rLL} );
     my $Num = @{$rLL};
 
-    my $K_opening_container = $self->[_K_opening_container_];
-    my $K_closing_container = $self->[_K_closing_container_];
+    my $K_opening_container  = $self->[_K_opening_container_];
+    my $K_closing_container  = $self->[_K_closing_container_];
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
 
     # We define an array of pairs of nested containers
     my @nested_pairs;
@@ -7188,6 +8210,9 @@ sub find_nested_pairs {
         my $K_inner_opening = $K_opening_container->{$inner_seqno};
         next unless defined($K_outer_opening) && defined($K_inner_opening);
 
+        my $inner_blocktype = $rblock_type_of_seqno->{$inner_seqno};
+        my $outer_blocktype = $rblock_type_of_seqno->{$outer_seqno};
+
         # Verify that the inner opening token is the next container after the
         # outer opening token.
         my $K_io_check = $rLL->[$K_outer_opening]->[_KNEXT_SEQ_ITEM_];
@@ -7210,7 +8235,7 @@ sub find_nested_pairs {
             #   | |
             #  ic oc
 
-            next if $rLL->[$K_inner_opening]->[_BLOCK_TYPE_] ne 'sub';
+            next if ( !$inner_blocktype || $inner_blocktype ne 'sub' );
             next if $rLL->[$K_io_check]->[_TOKEN_] ne '(';
             my $seqno_signature = $rLL->[$K_io_check]->[_TYPE_SEQUENCE_];
             next unless defined($seqno_signature);
@@ -7261,6 +8286,7 @@ sub find_nested_pairs {
         #       @array) )
         my $Kn_first = $K_outer_opening;
         my $Kn_last_nonblank;
+        my $saw_comment;
         for (
             my $Kn = $K_outer_opening + 1 ;
             $Kn <= $K_inner_opening ;
@@ -7275,7 +8301,8 @@ sub find_nested_pairs {
             # skip chain of identifier tokens
             my $last_type    = $type;
             my $last_is_name = $is_name;
-            $type    = $rLL->[$Kn]->[_TYPE_];
+            $type = $rLL->[$Kn]->[_TYPE_];
+            if ( $type eq '#' ) { $saw_comment = 1; last }
             $is_name = $is_name_type->{$type};
             next if ( $is_name && $last_is_name );
 
@@ -7283,6 +8310,9 @@ sub find_nested_pairs {
             last if ( $nonblank_count > 2 );
         }
 
+        # Do not weld across a comment .. fix for c058.
+        next if ($saw_comment);
+
         # Patch for b1104: do not weld to a paren preceded by sort/map/grep
         # because the special line break rules may cause a blinking state
         if (   defined($Kn_last_nonblank)
@@ -7309,9 +8339,10 @@ sub find_nested_pairs {
             # this
             #    sub make_anon_with_my_sub { sub {
             # because it probably hides the structure a little too much.
-            || (   $rLL->[$K_inner_opening]->[_BLOCK_TYPE_] eq 'sub'
+            || (   $inner_blocktype
+                && $inner_blocktype eq 'sub'
                 && $rLL->[$Kn_first]->[_TOKEN_] eq 'sub'
-                && !$rLL->[$K_outer_opening]->[_BLOCK_TYPE_] )
+                && !$outer_blocktype )
           )
         {
             push @nested_pairs,
@@ -7337,23 +8368,41 @@ sub find_nested_pairs {
     return \@nested_pairs;
 }
 
-sub is_excluded_weld {
+sub match_paren_flag {
+
+    # Decide if this paren is excluded by user request:
+    #   undef matches no parens
+    #   '*' matches all parens
+    #   'k' matches only if the previous nonblank token is a perl builtin
+    #       keyword (such as 'if', 'while'),
+    #   'K' matches if 'k' does not, meaning if the previous token is not a
+    #       keyword.
+    #   'f' matches if the previous token is a function other than a keyword.
+    #   'F' matches if 'f' does not.
+    #   'w' matches if either 'k' or 'f' match.
+    #   'W' matches if 'w' does not.
+    my ( $self, $KK, $flag ) = @_;
 
-    # decide if this weld is excluded by user request
-    my ( $self, $KK, $is_leading ) = @_;
-    my $rLL         = $self->[_rLL_];
-    my $rtoken_vars = $rLL->[$KK];
-    my $token       = $rtoken_vars->[_TOKEN_];
-    my $rflags      = $weld_nested_exclusion_rules{$token};
-    return 0 unless ( defined($rflags) );
-    my $flag = $is_leading ? $rflags->[0] : $rflags->[1];
     return 0 unless ( defined($flag) );
+    return 0 if $flag eq '0';
+    return 1 if $flag eq '1';
     return 1 if $flag eq '*';
+    return 0 unless ( defined($KK) );
+
+    my $rLL         = $self->[_rLL_];
+    my $rtoken_vars = $rLL->[$KK];
+    my $seqno       = $rtoken_vars->[_TYPE_SEQUENCE_];
+    return 0 unless ($seqno);
+    my $token     = $rtoken_vars->[_TOKEN_];
+    my $K_opening = $KK;
+    if ( !$is_opening_token{$token} ) {
+        $K_opening = $self->[_K_opening_container_]->{$seqno};
+    }
+    return unless ( defined($K_opening) );
 
     my ( $is_f, $is_k, $is_w );
-    my $Kp = $self->K_previous_nonblank($KK);
+    my $Kp = $self->K_previous_nonblank($K_opening);
     if ( defined($Kp) ) {
-        my $seqno  = $rtoken_vars->[_TYPE_SEQUENCE_];
         my $type_p = $rLL->[$Kp]->[_TYPE_];
 
         # keyword?
@@ -7365,7 +8414,6 @@ sub is_excluded_weld {
         # either keyword or function call?
         $is_w = $is_k || $is_f;
     }
-
     my $match;
     if    ( $flag eq 'k' ) { $match = $is_k }
     elsif ( $flag eq 'K' ) { $match = !$is_k }
@@ -7376,13 +8424,38 @@ sub is_excluded_weld {
     return $match;
 }
 
-# types needed for welding RULE 6
+sub is_excluded_weld {
+
+    # decide if this weld is excluded by user request
+    my ( $self, $KK, $is_leading ) = @_;
+    my $rLL         = $self->[_rLL_];
+    my $rtoken_vars = $rLL->[$KK];
+    my $token       = $rtoken_vars->[_TOKEN_];
+    my $rflags      = $weld_nested_exclusion_rules{$token};
+    return 0 unless ( defined($rflags) );
+    my $flag = $is_leading ? $rflags->[0] : $rflags->[1];
+    return 0 unless ( defined($flag) );
+    return 1 if $flag eq '*';
+    return $self->match_paren_flag( $KK, $flag );
+}
+
+# hashes to simplify welding logic
 my %type_ok_after_bareword;
+my %is_ternary;
+my %has_tight_paren;
 
 BEGIN {
 
+    # types needed for welding RULE 6
     my @q = qw# => -> { ( [ #;
     @type_ok_after_bareword{@q} = (1) x scalar(@q);
+
+    @q = qw( ? : );
+    @is_ternary{@q} = (1) x scalar(@q);
+
+    # these types do not 'like' to be separated from a following paren
+    @q = qw(w i q Q G C Z U);
+    @{has_tight_paren}{@q} = (1) x scalar(@q);
 }
 
 use constant DEBUG_WELD => 0;
@@ -7416,24 +8489,48 @@ sub setup_new_weld_measurements {
     my $rK_range = $rlines->[$iline_oo]->{_rK_range};
     my ( $Kfirst, $Klast ) = @{$rK_range};
 
-    # Define a reference index from which to start measuring
-    my $Kref  = $Kfirst;
+    #-------------------------------------------------------------------------
+    # We now define a reference index, '$Kref', from which to start measuring
+    # This choice turns out to be critical for keeping welds stable during
+    # iterations, so we go through a number of STEPS...
+    #-------------------------------------------------------------------------
+
+    # STEP 1: Our starting guess is to use measure from the first token of the
+    # current line.  This is usually a good guess.
+    my $Kref = $Kfirst;
+
+    # STEP 2: See if we should go back a little farther
     my $Kprev = $self->K_previous_nonblank($Kfirst);
     if ( defined($Kprev) ) {
 
-        # The -iob and -wn flags do not work well together. To avoid
-        # blinking states we have to override -iob at certain key line
-        # breaks.
-        $self->[_ris_essential_old_breakpoint_]->{$Kprev} = 1;
+        # Avoid measuring from between an opening paren and a previous token
+        # which should stay close to it ... fixes b1185
+        my $token_oo  = $rLL->[$Kouter_opening]->[_TOKEN_];
+        my $type_prev = $rLL->[$Kprev]->[_TYPE_];
+        if (   $Kouter_opening == $Kfirst
+            && $token_oo eq '('
+            && $has_tight_paren{$type_prev} )
+        {
+            $Kref = $Kprev;
+        }
 
         # Back up and count length from a token like '=' or '=>' if -lp
         # is used (this fixes b520)
         # ...or if a break is wanted before there
-        my $type_prev = $rLL->[$Kprev]->[_TYPE_];
-        if (   $rOpts_line_up_parentheses
+        elsif ($rOpts_line_up_parentheses
             || $want_break_before{$type_prev} )
         {
-            if ( substr( $type_prev, 0, 1 ) eq '=' ) {
+
+            # If there are other sequence items between the start of this line
+            # and the opening token in question, then do not include tokens on
+            # the previous line in length calculations.  This check added to
+            # fix case b1174 which had a '?' on the line
+            my $no_previous_seq_item = $Kref == $Kouter_opening
+              || $rLL->[$Kref]->[_KNEXT_SEQ_ITEM_] == $Kouter_opening;
+
+            if ( $no_previous_seq_item
+                && substr( $type_prev, 0, 1 ) eq '=' )
+            {
                 $Kref = $Kprev;
 
                 # Fix for b1144 and b1112: backup to the first nonblank
@@ -7452,6 +8549,26 @@ sub setup_new_weld_measurements {
         }
     }
 
+    # STEP 3: Now look ahead for a ternary and, if found, use it.
+    # This fixes case b1182.
+    # Also look for a ')' at the same level and, if found, use it.
+    # This fixes case b1224.
+    if ( $Kref < $Kouter_opening ) {
+        my $Knext    = $rLL->[$Kref]->[_KNEXT_SEQ_ITEM_];
+        my $level_oo = $rLL->[$Kouter_opening]->[_LEVEL_];
+        while ( $Knext < $Kouter_opening ) {
+            if ( $rLL->[$Knext]->[_LEVEL_] == $level_oo ) {
+                if (   $is_ternary{ $rLL->[$Knext]->[_TYPE_] }
+                    || $rLL->[$Knext]->[_TOKEN_] eq ')' )
+                {
+                    $Kref = $Knext;
+                    last;
+                }
+            }
+            $Knext = $rLL->[$Knext]->[_KNEXT_SEQ_ITEM_];
+        }
+    }
+
     # Define the starting measurements we will need
     $starting_lentot =
       $Kref <= 0 ? 0 : $rLL->[ $Kref - 1 ]->[_CUMULATIVE_LENGTH_];
@@ -7461,9 +8578,7 @@ sub setup_new_weld_measurements {
     $maximum_text_length = $maximum_text_length_at_level[$starting_level] -
       $starting_ci * $rOpts_continuation_indentation;
 
-    # Now fix these if necessary to avoid known problems...
-
-    # FIX1: Switch to using the outer opening token as the reference
+    # STEP 4: Switch to using the outer opening token as the reference
     # point if a line break before it would make a longer line.
     # Fixes case b1055 and is also an alternate fix for b1065.
     my $starting_level_oo = $rLL->[$Kouter_opening]->[_LEVEL_];
@@ -7492,7 +8607,7 @@ sub setup_new_weld_measurements {
 
     my $new_weld_ok = 1;
 
-    # FIX2 for b1020: Avoid problem areas with the -wn -lp combination.  The
+    # STEP 5, fix b1020: Avoid problem areas with the -wn -lp combination.  The
     # combination -wn -lp -dws -naws does not work well and can cause blinkers.
     # It will probably only occur in stress testing.  For this situation we
     # will only start a new weld if we start at a 'good' location.
@@ -7500,6 +8615,7 @@ sub setup_new_weld_measurements {
     # - Require blank before certain previous characters to fix b1111.
     # - Add ';' to fix case b1139
     # - Convert from '$ok_to_weld' to '$new_weld_ok' to fix b1162.
+    # - relaxed constraints for b1227
     if (   $starting_ci
         && $rOpts_line_up_parentheses
         && $rOpts_delete_old_whitespace
@@ -7512,20 +8628,23 @@ sub setup_new_weld_measurements {
         my $type_pp     = 'b';
         if ( $Kprev >= 0 ) { $type_pp = $rLL->[ $Kprev - 1 ]->[_TYPE_] }
         unless (
-               $type_prev  =~ /^[\,\.\;]/
-            || $type_prev  =~ /^[=\{\[\(\L]/ && $type_pp eq 'b'
+               $type_prev =~ /^[\,\.\;]/
+            || $type_prev =~ /^[=\{\[\(\L]/
+            && ( $type_pp eq 'b' || $type_pp eq '}' || $type_first eq 'k' )
             || $type_first =~ /^[=\,\.\;\{\[\(\L]/
             || $type_first eq '||'
-            || (   $type_first eq 'k' && $token_first eq 'if'
-                || $token_first eq 'or' )
+            || (
+                $type_first eq 'k'
+                && (   $token_first eq 'if'
+                    || $token_first eq 'or' )
+            )
           )
         {
             $msg =
-"Skipping weld: poor break with -lp and ci at type_first='$type_first' type_prev='$type_prev'\n";
+"Skipping weld: poor break with -lp and ci at type_first='$type_first' type_prev='$type_prev' type_pp=$type_pp\n";
             $new_weld_ok = 0;
         }
     }
-
     return ( $new_weld_ok, $maximum_text_length, $starting_lentot, $msg );
 }
 
@@ -7582,10 +8701,14 @@ sub weld_nested_containers {
     # involves setting certain hash values which will be checked
     # later during formatting.
 
-    my $rLL                 = $self->[_rLL_];
-    my $rlines              = $self->[_rlines_];
-    my $K_opening_container = $self->[_K_opening_container_];
-    my $K_closing_container = $self->[_K_closing_container_];
+    my $rLL                       = $self->[_rLL_];
+    my $rlines                    = $self->[_rlines_];
+    my $K_opening_container       = $self->[_K_opening_container_];
+    my $K_closing_container       = $self->[_K_closing_container_];
+    my $rblock_type_of_seqno      = $self->[_rblock_type_of_seqno_];
+    my $ris_excluded_lp_container = $self->[_ris_excluded_lp_container_];
+    my $ris_asub_block            = $self->[_ris_asub_block_];
+    my $rOpts_asbl = $rOpts->{'opening-anonymous-sub-brace-on-new-line'};
 
     # Find nested pairs of container tokens for any welding.
     my $rnested_pairs = $self->find_nested_pairs();
@@ -7606,8 +8729,29 @@ sub weld_nested_containers {
     my $iline_outer_opening   = -1;
     my $weld_count_this_start = 0;
 
-    my $multiline_tol =
-      1 + max( $rOpts_indent_columns, $rOpts_continuation_indentation );
+    # OLD: $single_line_tol added to fix cases b1180 b1181
+    #       = $rOpts_continuation_indentation > $rOpts_indent_columns ? 1 : 0;
+    # NEW: $single_line_tol=0;  fixes b1212 and b1180-1181 work now
+    my $single_line_tol = 0;
+
+    my $multiline_tol = $single_line_tol + 1 +
+      max( $rOpts_indent_columns, $rOpts_continuation_indentation );
+
+    # Define a welding cutoff level: do not start a weld if the inside
+    # container level equals or exceeds this level.
+
+    # We use the minimum of two criteria, either of which may be more
+    # restrictive.  The 'alpha' value is more restrictive in (b1206, b1252) and
+    # the 'beta' value is more restrictive in other cases (b1243).
+
+    my $weld_cutoff_level = min( $stress_level_alpha, $stress_level_beta + 3 );
+
+    # The vertical tightness flags can throw off line length calculations.
+    # This patch was added to fix instability issue b1284.
+    # It works to always use a tol of 1 for 1 line block length tests, but
+    # this restricted value keeps test case wn6.wn working as before.
+    # It may be necessary to include '[' and '{' here in the future.
+    my $one_line_tol = $opening_vertical_tightness{'('} ? 1 : 0;
 
     my $length_to_opening_seqno = sub {
         my ($seqno) = @_;
@@ -7685,6 +8829,14 @@ sub weld_nested_containers {
 
         }
 
+        # RULE: Avoid welding under stress.  The idea is that we need to have a
+        # little space* within a welded container to avoid instability.  Note
+        # that after each weld the level values are reduced, so long multiple
+        # welds can still be made.  This rule will seldom be a limiting factor
+        # in actual working code. Fixes b1206, b1243.
+        my $inner_level = $inner_opening->[_LEVEL_];
+        if ( $inner_level >= $weld_cutoff_level ) { next }
+
         # Set flag saying if this pair starts a new weld
         my $starting_new_weld = !( @welds && $outer_seqno == $welds[-1]->[0] );
 
@@ -7703,6 +8855,7 @@ sub weld_nested_containers {
         my $iline_ic = $inner_closing->[_LINE_INDEX_];
         my $iline_oc = $outer_closing->[_LINE_INDEX_];
         my $token_oo = $outer_opening->[_TOKEN_];
+        my $token_io = $inner_opening->[_TOKEN_];
 
         my $is_multiline_weld =
              $iline_oo == $iline_io
@@ -7710,9 +8863,8 @@ sub weld_nested_containers {
           && $iline_io != $iline_ic;
 
         if (DEBUG_WELD) {
-            my $token_io = $rLL->[$Kinner_opening]->[_TOKEN_];
-            my $len_oo   = $rLL->[$Kouter_opening]->[_CUMULATIVE_LENGTH_];
-            my $len_io   = $rLL->[$Kinner_opening]->[_CUMULATIVE_LENGTH_];
+            my $len_oo = $rLL->[$Kouter_opening]->[_CUMULATIVE_LENGTH_];
+            my $len_io = $rLL->[$Kinner_opening]->[_CUMULATIVE_LENGTH_];
             $Msg .= <<EOM;
 Pair seqo=$outer_seqno seqi=$inner_seqno  lines: loo=$iline_oo lio=$iline_io lic=$iline_ic loc=$iline_oc
 Koo=$Kouter_opening Kio=$Kinner_opening Kic=$Kinner_closing Koc=$Kouter_closing lenoo=$len_oo lenio=$len_io
@@ -7720,8 +8872,24 @@ tokens '$token_oo' .. '$token_io'
 EOM
         }
 
-        # If this pair is not adjacent to the previous pair (skipped or not),
-        # then measure lengths from the start of line of oo.
+        # DO-NOT-WELD RULE 0:
+        # Avoid a new paren-paren weld if inner parens are 'sheared' (separated
+        # by one line).  This can produce instabilities (fixes b1250 b1251
+        # 1256).
+        if (  !$is_multiline_weld
+            && $iline_ic == $iline_io + 1
+            && $token_oo eq '('
+            && $token_io eq '(' )
+        {
+            if (DEBUG_WELD) {
+                $Msg .= "RULE 0: Not welding due to sheared inner parens\n";
+                print $Msg;
+            }
+            next;
+        }
+
+        # If this pair is not adjacent to the previous pair (skipped or not),
+        # then measure lengths from the start of line of oo.
         if (
             !$touch_previous_pair
 
@@ -7753,52 +8921,76 @@ EOM
 
             # An existing one-line weld is a line in which
             # (1) the containers are all on one line, and
-            # (2) the line does not exceed the allowable length, and
-            # This flag is used to avoid creating blinkers.
-            # FIX1: Changed 'excess_length_to_K' to 'excess_length_of_line'
-            # to get exact lengths and fix b604 b605.
+            # (2) the line does not exceed the allowable length
             if ( $iline_oo == $iline_oc ) {
 
-                # All the tokens are on one line, now check their length
-                my $excess =
-                  $self->excess_line_length_for_Krange( $Kfirst, $Klast );
-                if ( $excess <= 0 ) {
-
-                    # All tokens are on one line and fit. This is a valid
-                    # existing one-line weld except for some edge cases
-                    # involving -lp:
-
-                    # FIX2: Patch for b1114: add a tolerance of one level if
-                    # this line has an unbalanced start.  This helps prevent
-                    # blinkers in unusual cases for lines near the length limit
-                    # by making it more likely that RULE 2 will prevent a weld.
-                    # FIX3: for b1131: only use level difference in -lp mode.
-                    # FIX4: for b1141, b1142: reduce the tolerance for longer
-                    # leading tokens
-                    if (   $rOpts_line_up_parentheses
-                        && $outer_opening->[_LEVEL_] -
-                        $rLL->[$Kfirst]->[_LEVEL_] )
+                # All the tokens are on one line, now check their length.
+                # Start with the full line index range. We will reduce this
+                # in the coding below in some cases.
+                my $Kstart = $Kfirst;
+                my $Kstop  = $Klast;
+
+                # Note that the following minimal choice for measuring will
+                # work and will not cause any instabilities because it is
+                # invariant:
+
+                ##  my $Kstart = $Kouter_opening;
+                ##  my $Kstop  = $Kouter_closing;
+
+                # But that can lead to some undesirable welds.  So a little
+                # more complicated method has been developed.
+
+                # We are trying to avoid creating bad two-line welds when we are
+                # working on long, previously unwelded input text, such as
+
+                # INPUT (example of a long input line weld candidate):
+                ## $mutation->transpos( $self->RNA->position($mutation->label, $atg_label));
+
+                #  GOOD two-line break: (not welded; result marked too long):
+                ## $mutation->transpos(
+                ##                 $self->RNA->position($mutation->label, $atg_label));
+
+                #  BAD two-line break: (welded; result if we weld):
+                ## $mutation->transpos($self->RNA->position(
+                ##                                      $mutation->label, $atg_label));
+
+                # We can only get an approximate estimate of the final length,
+                # since the line breaks may change, and for -lp mode because
+                # even the indentation is not yet known.
+
+                my $level_first = $rLL->[$Kfirst]->[_LEVEL_];
+                my $level_last  = $rLL->[$Klast]->[_LEVEL_];
+                my $level_oo    = $rLL->[$Kouter_opening]->[_LEVEL_];
+                my $level_oc    = $rLL->[$Kouter_closing]->[_LEVEL_];
+
+                # - measure to the end of the original line if balanced
+                # - measure to the closing container if unbalanced (fixes b1230)
+                #if ( $level_first != $level_last ) { $Kstop = $Kouter_closing }
+                if ( $level_oc > $level_last ) { $Kstop = $Kouter_closing }
+
+                # - measure from the start of the original line if balanced
+                # - measure from the most previous token with same level
+                #   if unbalanced (b1232)
+                if ( $Kouter_opening > $Kfirst && $level_oo > $level_first ) {
+                    $Kstart = $Kouter_opening;
+                    for (
+                        my $KK = $Kouter_opening - 1 ;
+                        $KK > $Kfirst ;
+                        $KK -= 1
+                      )
                     {
-
-                        # We only need a tolerance if the leading text before
-                        # the first opening token is shorter than the
-                        # indentation length.  For simplicity we just use the
-                        # length of the first token here.  If necessary, we
-                        # could be more exact in the future and find the
-                        # total length up to the first opening token.
-                        # See cases b1114, b1141, b1142.
-                        my $tolx = max( 0,
-                            $rOpts_indent_columns -
-                              $rLL->[$Kfirst]->[_TOKEN_LENGTH_] );
-
-                        if ( $excess + $tolx <= 0 ) {
-                            $is_one_line_weld = 1;
-                        }
-                    }
-                    else {
-                        $is_one_line_weld = 1;
+                        next if ( $rLL->[$KK]->[_TYPE_] eq 'b' );
+                        last if ( $rLL->[$KK]->[_LEVEL_] < $level_oo );
+                        $Kstart = $KK;
                     }
                 }
+
+                my $excess =
+                  $self->excess_line_length_for_Krange( $Kstart, $Kstop );
+
+                # Coding simplified here for case b1219.
+                # Increased tol from 0 to 1 when pvt>0 to fix b1284.
+                $is_one_line_weld = $excess <= $one_line_tol;
             }
 
             # DO-NOT-WELD RULE 1:
@@ -7823,7 +9015,6 @@ EOM
 
             # $top_label->set_text( gettext(
             #    "Unable to create personal directory - check permissions.") );
-
             if (   $iline_oc == $iline_oo + 1
                 && $iline_io == $iline_ic
                 && $token_oo eq '(' )
@@ -7847,6 +9038,12 @@ EOM
             }
         } ## end starting new weld sequence
 
+        else {
+
+            # set the 1-line flag if continuing a weld sequence; fixes b1239
+            $is_one_line_weld = ( $iline_oo == $iline_oc );
+        }
+
         # DO-NOT-WELD RULE 2:
         # Do not weld an opening paren to an inner one line brace block
         # We will just use old line numbers for this test and require
@@ -7857,7 +9054,7 @@ EOM
         # as here:
 
         #    $_[0]->code_handler
-        #      ( sub { $more .= $_[1] . ":" . $_[0] . "\n" } );
+        #      ( sub { $more .= $_[1] . ":" . $_[0] . "\n" } );
 
         # Here is another example where we do not want to weld:
         #  $wrapped->add_around_modifier(
@@ -7886,7 +9083,6 @@ EOM
         # The effect of this change on typical code is very minimal.  Sometimes
         # it may take a second iteration to converge, but this gives protection
         # against blinking.
-
         if (   !$do_not_weld_rule
             && !$is_one_line_weld
             && $iline_ic == $iline_io )
@@ -7895,6 +9091,24 @@ EOM
               if ( $token_oo eq '(' || $iline_oo != $iline_io );
         }
 
+        # DO-NOT-WELD RULE 2A:
+        # Do not weld an opening asub brace in -lp mode if -asbl is set. This
+        # helps avoid instabilities in one-line block formation, and fixes
+        # b1241.  Previously, the '$is_one_line_weld' flag was tested here
+        # instead of -asbl, and this fixed most cases. But it turns out that
+        # the real problem was the -asbl flag, and switching to this was
+        # necessary to fixe b1268.  This also fixes b1269, b1277, b1278.
+        if (
+            !$do_not_weld_rule
+            ##&& $is_one_line_weld
+            && $rOpts_line_up_parentheses
+            && $rOpts_asbl
+            && $ris_asub_block->{$outer_seqno}
+          )
+        {
+            $do_not_weld_rule = '2A';
+        }
+
         # DO-NOT-WELD RULE 3:
         # Do not weld if this makes our line too long.
         # Use a tolerance which depends on if the old tokens were welded
@@ -7930,8 +9144,8 @@ EOM
             # We can use zero tolerance if it looks like we are working on an
             # existing weld.
             my $tol =
-              $is_one_line_weld || $is_multiline_weld
-              ? 0
+                $is_one_line_weld || $is_multiline_weld
+              ? $single_line_tol
               : $multiline_tol;
 
             # By how many characters does this exceed the text window?
@@ -7972,7 +9186,7 @@ EOM
 
         # then we will do the weld and retain the one-line block
         if ( !$do_not_weld_rule && $rOpts->{'cuddled-else'} ) {
-            my $block_type = $rLL->[$Kouter_opening]->[_BLOCK_TYPE_];
+            my $block_type = $rblock_type_of_seqno->{$outer_seqno};
             if ( $block_type && $rcuddled_block_types->{'*'}->{$block_type} ) {
                 my $io_line = $inner_opening->[_LINE_INDEX_];
                 my $ic_line = $inner_closing->[_LINE_INDEX_];
@@ -8017,14 +9231,20 @@ EOM
 
         if ($do_not_weld_rule) {
 
-            # After neglecting a pair, we start measuring from start of point io
-            my $starting_level    = $inner_opening->[_LEVEL_];
-            my $starting_ci_level = $inner_opening->[_CI_LEVEL_];
-            $starting_lentot =
-              $self->cumulative_length_before_K($Kinner_opening);
-            $maximum_text_length =
-              $maximum_text_length_at_level[$starting_level] -
-              $starting_ci_level * $rOpts_continuation_indentation;
+            # After neglecting a pair, we start measuring from start of point
+            # io ... but not if previous type does not like to be separated
+            # from its container (fixes case b1184)
+            my $Kprev     = $self->K_previous_nonblank($Kinner_opening);
+            my $type_prev = defined($Kprev) ? $rLL->[$Kprev]->[_TYPE_] : 'w';
+            if ( !$has_tight_paren{$type_prev} ) {
+                my $starting_level    = $inner_opening->[_LEVEL_];
+                my $starting_ci_level = $inner_opening->[_CI_LEVEL_];
+                $starting_lentot =
+                  $self->cumulative_length_before_K($Kinner_opening);
+                $maximum_text_length =
+                  $maximum_text_length_at_level[$starting_level] -
+                  $starting_ci_level * $rOpts_continuation_indentation;
+            }
 
             if (DEBUG_WELD) {
                 $Msg .= "Not welding due to RULE $do_not_weld_rule\n";
@@ -8150,7 +9370,9 @@ sub weld_nested_quotes {
             # the bottom of sub 'respace_tokens' which set the values of
             # _KNEXT_SEQ_ITEM_.  Or an error has been introduced in the
             # loop control lines above.
-            Fault("sequence = $outer_seqno not defined at K=$KK");
+            Fault("sequence = $outer_seqno not defined at K=$KK")
+              if (DEVEL_MODE);
+            next;
         }
 
         my $token = $rtoken_vars->[_TOKEN_];
@@ -8211,6 +9433,19 @@ sub weld_nested_quotes {
             my $is_old_weld =
               ( $iline_oo == $iline_io && $iline_ic == $iline_oc );
 
+            # Fix for case b1189. If quote is marked as type 'Q' then only weld
+            # if the two closing tokens are on the same input line.  Otherwise,
+            # the closing line will be output earlier in the pipeline than
+            # other CODE lines and welding will not actually occur. This will
+            # leave a half-welded structure with potential formatting
+            # instability.  This might be fixed by adding a check for a weld on
+            # a closing Q token and sending it down the normal channel, but it
+            # would complicate the code and is potentially risky.
+            next
+              if (!$is_old_weld
+                && $next_type eq 'Q'
+                && $iline_ic != $iline_oc );
+
             # If welded, the line must not exceed allowed line length
             ( my $ok_to_weld, $maximum_text_length, $starting_lentot, my $msg )
               = $self->setup_new_weld_measurements( $Kouter_opening,
@@ -8326,18 +9561,6 @@ sub weld_nested_quotes {
     return;
 }
 
-sub is_welded_right_at_i {
-    my ( $self, $i ) = @_;
-    return unless ( $total_weld_count && $i >= 0 );
-
-    # Back up at a blank.  This routine is sometimes called at blanks.
-    # TODO: this routine can eventually be eliminated by setting the weld flags
-    # for all K indexes between the start and end of a weld, not just at
-    # sequenced items.
-    if ( $i > 0 && $types_to_go[$i] eq 'b' ) { $i-- }
-    return defined( $self->[_rK_weld_right_]->{ $K_to_go[$i] } );
-}
-
 sub is_welded_at_seqno {
 
     my ( $self, $seqno ) = @_;
@@ -8382,11 +9605,12 @@ sub mark_short_nested_blocks {
 
     return unless ( $rOpts->{'one-line-block-nesting'} );
 
-    my $K_opening_container = $self->[_K_opening_container_];
-    my $K_closing_container = $self->[_K_closing_container_];
-    my $rbreak_container    = $self->[_rbreak_container_];
-    my $rshort_nested       = $self->[_rshort_nested_];
-    my $rlines              = $self->[_rlines_];
+    my $K_opening_container  = $self->[_K_opening_container_];
+    my $K_closing_container  = $self->[_K_closing_container_];
+    my $rbreak_container     = $self->[_rbreak_container_];
+    my $rshort_nested        = $self->[_rshort_nested_];
+    my $rlines               = $self->[_rlines_];
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
 
     # Variables needed for estimating line lengths
     my $maximum_text_length;
@@ -8431,7 +9655,9 @@ sub mark_short_nested_blocks {
             # the bottom of sub 'respace_tokens' which set the values of
             # _KNEXT_SEQ_ITEM_.  Or an error has been introduced in the
             # loop control lines above.
-            Fault("sequence = $type_sequence not defined at K=$KK");
+            Fault("sequence = $type_sequence not defined at K=$KK")
+              if (DEVEL_MODE);
+            next;
         }
 
         # Patch: do not mark short blocks with welds.
@@ -8444,8 +9670,7 @@ sub mark_short_nested_blocks {
         my $token = $rtoken_vars->[_TOKEN_];
         my $type  = $rtoken_vars->[_TYPE_];
         next unless ( $type eq $token );
-        my $block_type = $rtoken_vars->[_BLOCK_TYPE_];
-        next unless ($block_type);
+        next unless ( $rblock_type_of_seqno->{$type_sequence} );
 
         # Keep a stack of all acceptable block braces seen.
         # Only consider blocks entirely on one line so dump the stack when line
@@ -8513,9 +9738,13 @@ sub adjust_indentation_levels {
     # levels.  It would be much nicer to have the weld routines also use this
     # adjustment, but that gets complicated when we combine -gnu -wn and have
     # some welded quotes.
-    my $radjusted_levels = $self->[_radjusted_levels_];
+    my $Klimit           = $self->[_Klimit_];
     my $rLL              = $self->[_rLL_];
-    foreach my $KK ( 0 .. @{$rLL} - 1 ) {
+    my $radjusted_levels = $self->[_radjusted_levels_];
+
+    return unless ( defined($Klimit) );
+
+    foreach my $KK ( 0 .. $Klimit ) {
         $radjusted_levels->[$KK] = $rLL->[$KK]->[_LEVEL_];
     }
 
@@ -8528,11 +9757,13 @@ sub adjust_indentation_levels {
     # Set adjusted levels for the whitespace cycle option.
     $self->whitespace_cycle_adjustment();
 
+    $self->braces_left_setup();
+
     # Adjust continuation indentation if -bli is set
     $self->bli_adjustment();
 
     $self->extended_ci()
-      if ( $rOpts->{'extended-continuation-indentation'} );
+      if ($rOpts_extended_continuation_indentation);
 
     # Now clip any adjusted levels to be non-negative
     $self->clip_adjusted_levels();
@@ -8561,32 +9792,30 @@ sub non_indenting_braces {
     my $rLL = $self->[_rLL_];
     return unless ( defined($rLL) && @{$rLL} );
 
+    my $Klimit                     = $self->[_Klimit_];
+    my $rblock_type_of_seqno       = $self->[_rblock_type_of_seqno_];
+    my $K_opening_container        = $self->[_K_opening_container_];
+    my $K_closing_container        = $self->[_K_closing_container_];
     my $rspecial_side_comment_type = $self->[_rspecial_side_comment_type_];
+    my $radjusted_levels           = $self->[_radjusted_levels_];
 
-    my $radjusted_levels = $self->[_radjusted_levels_];
-    my $Kmax             = @{$rLL} - 1;
-    my @seqno_stack;
-
-    my $is_non_indenting_brace = sub {
-        my ($KK) = @_;
-
-        # looking for an opening block brace
-        my $token      = $rLL->[$KK]->[_TOKEN_];
-        my $block_type = $rLL->[$KK]->[_BLOCK_TYPE_];
-        return unless ( $token eq '{' && $block_type );
+    # First locate all of the marked blocks
+    my @K_stack;
+    foreach my $seqno ( keys %{$rblock_type_of_seqno} ) {
+        my $KK = $K_opening_container->{$seqno};
 
         # followed by a comment
         my $K_sc = $KK + 1;
         $K_sc += 1
-          if ( $K_sc <= $Kmax && $rLL->[$K_sc]->[_TYPE_] eq 'b' );
-        return unless ( $K_sc <= $Kmax );
+          if ( $K_sc <= $Klimit && $rLL->[$K_sc]->[_TYPE_] eq 'b' );
+        next unless ( $K_sc <= $Klimit );
         my $type_sc = $rLL->[$K_sc]->[_TYPE_];
-        return unless ( $type_sc eq '#' );
+        next unless ( $type_sc eq '#' );
 
         # on the same line
         my $line_index    = $rLL->[$KK]->[_LINE_INDEX_];
         my $line_index_sc = $rLL->[$K_sc]->[_LINE_INDEX_];
-        return unless ( $line_index_sc == $line_index );
+        next unless ( $line_index_sc == $line_index );
 
         # get the side comment text
         my $token_sc = $rLL->[$K_sc]->[_TOKEN_];
@@ -8595,26 +9824,33 @@ sub non_indenting_braces {
         # we added it back for the match. That way we require an exact
         # match to the special string and also allow additional text.
         $token_sc .= "\n";
-        my $is_nib = ( $token_sc =~ /$non_indenting_brace_pattern/ );
-        if ($is_nib) { $rspecial_side_comment_type->{$K_sc} = 'NIB' }
-        return $is_nib;
-    };
+        next unless ( $token_sc =~ /$non_indenting_brace_pattern/ );
+        $rspecial_side_comment_type->{$K_sc} = 'NIB';
+        push @K_stack, [ $KK, 1 ];
+        my $Kc = $K_closing_container->{$seqno};
+        push @K_stack, [ $Kc, -1 ] if ( defined($Kc) );
+    }
+    return unless (@K_stack);
+    @K_stack = sort { $a->[0] <=> $b->[0] } @K_stack;
 
-    foreach my $KK ( 0 .. $Kmax ) {
-        my $num   = @seqno_stack;
-        my $seqno = $rLL->[$KK]->[_TYPE_SEQUENCE_];
-        if ($seqno) {
-            my $token = $rLL->[$KK]->[_TOKEN_];
-            if ( $token eq '{' && $is_non_indenting_brace->($KK) ) {
-                push @seqno_stack, $seqno;
-            }
-            if ( $token eq '}' && @seqno_stack && $seqno_stack[-1] == $seqno ) {
-                pop @seqno_stack;
-                $num -= 1;
+    # Then loop to remove indentation within marked blocks
+    my $KK_last = 0;
+    my $ndeep   = 0;
+    foreach my $item (@K_stack) {
+        my ( $KK, $inc ) = @{$item};
+        if ( $ndeep > 0 ) {
+
+            foreach ( $KK_last + 1 .. $KK ) {
+                $radjusted_levels->[$_] -= $ndeep;
             }
+
+            # We just subtracted the old $ndeep value, which only applies to a
+            # '{'.  The new $ndeep applies to a '}', so we undo the error.
+            if ( $inc < 0 ) { $radjusted_levels->[$KK] += 1 }
         }
-        next unless $num;
-        $radjusted_levels->[$KK] -= $num;
+
+        $ndeep += $inc;
+        $KK_last = $KK;
     }
     return;
 }
@@ -8627,9 +9863,12 @@ sub whitespace_cycle_adjustment {
     my $rLL = $self->[_rLL_];
     return unless ( defined($rLL) && @{$rLL} );
     my $radjusted_levels = $self->[_radjusted_levels_];
+    my $maximum_level    = $self->[_maximum_level_];
 
-    my $rOpts_whitespace_cycle = $rOpts->{'whitespace-cycle'};
-    if ( $rOpts_whitespace_cycle && $rOpts_whitespace_cycle > 0 ) {
+    if (   $rOpts_whitespace_cycle
+        && $rOpts_whitespace_cycle > 0
+        && $rOpts_whitespace_cycle < $maximum_level )
+    {
 
         my $Kmax = @{$rLL} - 1;
 
@@ -8717,23 +9956,27 @@ sub break_before_list_opening_containers {
     my $rlec_count_by_seqno       = $self->[_rlec_count_by_seqno_];
     my $rno_xci_by_seqno          = $self->[_rno_xci_by_seqno_];
     my $rK_weld_right             = $self->[_rK_weld_right_];
+    my $rblock_type_of_seqno      = $self->[_rblock_type_of_seqno_];
 
     my $length_tol =
       max( 1, $rOpts_continuation_indentation, $rOpts_indent_columns );
     if ($rOpts_ignore_old_breakpoints) {
-        $length_tol += $rOpts_maximum_line_length;
+
+        # Patch suggested by b1231; the old tol was excessive.
+        ## $length_tol += $rOpts_maximum_line_length;
+        $length_tol *= 2;
     }
 
     my $rbreak_before_container_by_seqno = {};
     my $rwant_reduced_ci                 = {};
     foreach my $seqno ( keys %{$K_opening_container} ) {
 
-        #################################################################
+        #----------------------------------------------------------------
         # Part 1: Examine any -bbx=n flags
-        #################################################################
+        #----------------------------------------------------------------
 
+        next if ( $rblock_type_of_seqno->{$seqno} );
         my $KK = $K_opening_container->{$seqno};
-        next if ( $rLL->[$KK]->[_BLOCK_TYPE_] );
 
         # This must be a list or contain a list.
         # Note1: switched from 'has_broken_list' to 'has_list' to fix b1024.
@@ -8764,6 +10007,15 @@ sub break_before_list_opening_containers {
         my $break_option = $break_before_container_types{$token};
         next unless ($break_option);
 
+        # Do not use -bbx under stress for stability ... fixes b1300
+        my $level = $rLL->[$KK]->[_LEVEL_];
+        if ( $level >= $stress_level_beta ) {
+            DEBUG_BBX
+              && print
+"BBX: Switching off at $seqno: level=$level exceeds beta stress level=$stress_level_beta\n";
+            next;
+        }
+
         # Require previous nonblank to be '=' or '=>'
         my $Kprev = $KK - 1;
         next if ( $Kprev < 0 );
@@ -8777,6 +10029,69 @@ sub break_before_list_opening_containers {
 
         my $ci = $rLL->[$KK]->[_CI_LEVEL_];
 
+        #--------------------------------------------
+        # New coding for option 2 (break if complex).
+        #--------------------------------------------
+        # This new coding uses clues which are invariant under formatting to
+        # decide if a list is complex.  For now it is only applied when -lp
+        # and -vmll are used, but eventually it may become the standard method.
+        # Fixes b1274, b1275, and others, including b1099.
+        if ( $break_option == 2 ) {
+
+            if (   $rOpts_line_up_parentheses
+                || $rOpts_variable_maximum_line_length )
+            {
+
+                # Start with the basic definition of a complex list...
+                my $is_complex = $is_list && $has_list;
+
+                # and it is also complex if the parent is a list
+                if ( !$is_complex ) {
+                    my $parent = $rparent_of_seqno->{$seqno};
+                    if ( $self->is_list_by_seqno($parent) ) {
+                        $is_complex = 1;
+                    }
+                }
+
+                # finally, we will call it complex if there are inner opening
+                # and closing container tokens, not parens, within the outer
+                # container tokens.
+                if ( !$is_complex ) {
+                    my $Kp      = $self->K_next_nonblank($KK);
+                    my $token_p = defined($Kp) ? $rLL->[$Kp]->[_TOKEN_] : 'b';
+                    if ( $is_opening_token{$token_p} && $token_p ne '(' ) {
+
+                        my $Kc = $K_closing_container->{$seqno};
+                        my $Km = $self->K_previous_nonblank($Kc);
+                        my $token_m =
+                          defined($Km) ? $rLL->[$Km]->[_TOKEN_] : 'b';
+
+                        # ignore any optional ending comma
+                        if ( $token_m eq ',' ) {
+                            $Km = $self->K_previous_nonblank($Km);
+                            $token_m =
+                              defined($Km) ? $rLL->[$Km]->[_TOKEN_] : 'b';
+                        }
+
+                        $is_complex ||=
+                          $is_closing_token{$token_m} && $token_m ne ')';
+                    }
+                }
+
+                # Convert to option 3 (always break) if complex
+                next unless ($is_complex);
+                $break_option = 3;
+            }
+        }
+
+        # Fix for b1231: the has_list_with_lec does not cover all cases.
+        # A broken container containing a list and with line-ending commas
+        # will stay broken, so can be treated as if it had a list with lec.
+        $has_list_with_lec ||=
+             $has_list
+          && $ris_broken_container->{$seqno}
+          && $rlec_count_by_seqno->{$seqno};
+
         DEBUG_BBX
           && print STDOUT
 "BBX: Looking at seqno=$seqno, token = $token with option=$break_option\n";
@@ -8818,15 +10133,6 @@ sub break_before_list_opening_containers {
                 }
             }
 
-            # Patch to fix b1099 for -lp
-            #  ok in -lp mode if this is a list which contains a list
-            if ( !$ok_to_break && $rOpts_line_up_parentheses ) {
-                if ( $is_list && $has_list ) {
-                    $ok_to_break = 1;
-                    DEBUG_BBX && do { $Msg = "is list or has list" };
-                }
-            }
-
             if ( !$ok_to_break ) {
                 DEBUG_BBX
                   && print STDOUT "Not breaking at seqno=$seqno: $Msg\n";
@@ -8863,7 +10169,7 @@ sub break_before_list_opening_containers {
         next unless ($ci_flag);
 
         # -bbxi=1: This option removes ci and is handled in
-        # later sub set_adjusted_indentation
+        # later sub final_indentation_adjustment
         if ( $ci_flag == 1 ) {
             $rwant_reduced_ci->{$seqno} = 1;
             next;
@@ -8871,9 +10177,9 @@ sub break_before_list_opening_containers {
 
         # -bbxi=2 ...
 
-        #################################################################
+        #----------------------------------------------------------------
         # Part 2: Perform tests before committing to changing ci and level
-        #################################################################
+        #----------------------------------------------------------------
 
         # Before changing the ci level of the opening container, we need
         # to be sure that the container will be broken in the later stages of
@@ -8888,6 +10194,12 @@ sub break_before_list_opening_containers {
         # Only consider containers already broken
         next if ( !$ris_broken_container->{$seqno} );
 
+        # Patch to fix issue b1305: the combination of -naws and ci>i appears
+        # to cause an instability.  It should almost never occur in practice.
+        next
+          if (!$rOpts_add_whitespace
+            && $rOpts_continuation_indentation > $rOpts_indent_columns );
+
         # Always ok to change ci for permanently broken containers
         if ( $ris_permanently_broken->{$seqno} ) {
             goto OK;
@@ -8921,7 +10233,6 @@ sub break_before_list_opening_containers {
         # single line.  Use the least possble indentation in the estmate (ci=0),
         # so we are not subtracting $ci * $rOpts_continuation_indentation from
         # tablulated $maximum_text_length  value.
-        my $level               = $rLL->[$KK]->[_LEVEL_];
         my $maximum_text_length = $maximum_text_length_at_level[$level];
         my $K_closing           = $K_closing_container->{$seqno};
         my $length = $self->cumulative_length_before_K($K_closing) -
@@ -9025,6 +10336,7 @@ sub extended_ci {
     my $rlines                   = $self->[_rlines_];
     my $rno_xci_by_seqno         = $self->[_rno_xci_by_seqno_];
     my $ris_bli_container        = $self->[_ris_bli_container_];
+    my $rblock_type_of_seqno     = $self->[_rblock_type_of_seqno_];
 
     my %available_space;
 
@@ -9093,7 +10405,7 @@ sub extended_ci {
         # Certain block types arrive from the tokenizer without CI but should
         # have it for this option.  These include anonymous subs and
         #     do sort map grep eval
-        my $block_type = $rLL->[$KK]->[_BLOCK_TYPE_];
+        my $block_type = $rblock_type_of_seqno->{$seqno};
         if ( $block_type && $is_block_with_ci{$block_type} ) {
             $rLL->[$KK]->[_CI_LEVEL_] = 1;
             if ($seqno_top) {
@@ -9157,6 +10469,15 @@ sub extended_ci {
           $maximum_text_length_at_level[$level] -
           $ci_level * $rOpts_continuation_indentation;
 
+        # Fix for b1197 b1198 b1199 b1200 b1201 b1202
+        # Do not apply -xci if we are running out of space
+        if ( $level >= $stress_level_beta ) {
+            DEBUG_XCI
+              && print
+"XCI: Skipping seqno=$seqno, level=$level exceeds stress level=$stress_level_beta\n";
+            next;
+        }
+
         # remember how much space is available for patch b1031 above
         my $space =
           $maximum_text_length - $len_tol - $rOpts_continuation_indentation;
@@ -9176,6 +10497,56 @@ sub extended_ci {
     return;
 }
 
+sub braces_left_setup {
+
+    # Called once per file to mark all -bl, -sbl, and -asbl containers
+    my $self = shift;
+
+    my $rOpts_bl   = $rOpts->{'opening-brace-on-new-line'};
+    my $rOpts_sbl  = $rOpts->{'opening-sub-brace-on-new-line'};
+    my $rOpts_asbl = $rOpts->{'opening-anonymous-sub-brace-on-new-line'};
+    return unless ( $rOpts_bl || $rOpts_sbl || $rOpts_asbl );
+
+    my $rLL = $self->[_rLL_];
+    return unless ( defined($rLL) && @{$rLL} );
+
+    # We will turn on this hash for braces controlled by these flags:
+    my $rbrace_left = $self->[_rbrace_left_];
+
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
+    my $ris_asub_block       = $self->[_ris_asub_block_];
+    my $ris_sub_block        = $self->[_ris_sub_block_];
+    foreach my $seqno ( keys %{$rblock_type_of_seqno} ) {
+
+        my $block_type = $rblock_type_of_seqno->{$seqno};
+
+        # use -asbl flag for an anonymous sub block
+        if ( $ris_asub_block->{$seqno} ) {
+            if ($rOpts_asbl) {
+                $rbrace_left->{$seqno} = 1;
+            }
+        }
+
+        # use -sbl flag for a named sub
+        elsif ( $ris_sub_block->{$seqno} ) {
+            if ($rOpts_sbl) {
+                $rbrace_left->{$seqno} = 1;
+            }
+        }
+
+        # use -bl flag if not a sub block of any type
+        else {
+            if (   $rOpts_bl
+                && $block_type =~ /$bl_pattern/
+                && $block_type !~ /$bl_exclusion_pattern/ )
+            {
+                $rbrace_left->{$seqno} = 1;
+            }
+        }
+    }
+    return;
+}
+
 sub bli_adjustment {
 
     # Called once per file to implement the --brace-left-and-indent option.
@@ -9184,26 +10555,25 @@ sub bli_adjustment {
     return unless ( $rOpts->{'brace-left-and-indent'} );
     my $rLL = $self->[_rLL_];
     return unless ( defined($rLL) && @{$rLL} );
-    my $ris_bli_container   = $self->[_ris_bli_container_];
-    my $K_opening_container = $self->[_K_opening_container_];
-    my $KNEXT               = $self->[_K_first_seq_item_];
 
-    while ( defined($KNEXT) ) {
-        my $KK = $KNEXT;
-        $KNEXT = $rLL->[$KNEXT]->[_KNEXT_SEQ_ITEM_];
-        my $block_type = $rLL->[$KK]->[_BLOCK_TYPE_];
-        if ( $block_type && $block_type =~ /$bli_pattern/ ) {
-            my $seqno     = $rLL->[$KK]->[_TYPE_SEQUENCE_];
-            my $K_opening = $K_opening_container->{$seqno};
-            if ( defined($K_opening) ) {
-                if ( $KK eq $K_opening ) {
-                    $rLL->[$KK]->[_CI_LEVEL_]++;
-                    $ris_bli_container->{$seqno} = 1;
-                }
-                else {
-                    $rLL->[$KK]->[_CI_LEVEL_] =
-                      $rLL->[$K_opening]->[_CI_LEVEL_];
-                }
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
+    my $ris_bli_container    = $self->[_ris_bli_container_];
+    my $rbrace_left          = $self->[_rbrace_left_];
+    my $K_opening_container  = $self->[_K_opening_container_];
+    my $K_closing_container  = $self->[_K_closing_container_];
+
+    foreach my $seqno ( keys %{$rblock_type_of_seqno} ) {
+        my $block_type = $rblock_type_of_seqno->{$seqno};
+        if (   $block_type
+            && $block_type =~ /$bli_pattern/
+            && $block_type !~ /$bli_exclusion_pattern/ )
+        {
+            $ris_bli_container->{$seqno} = 1;
+            $rbrace_left->{$seqno}       = 1;
+            my $Ko = $K_opening_container->{$seqno};
+            my $Kc = $K_closing_container->{$seqno};
+            if ( defined($Ko) && defined($Kc) ) {
+                $rLL->[$Kc]->[_CI_LEVEL_] = ++$rLL->[$Ko]->[_CI_LEVEL_];
             }
         }
     }
@@ -9285,7 +10655,7 @@ EOM
     # Give multiline qw lists extra indentation instead of CI.  This option
     # works well but is currently only activated when the -xci flag is set.
     # The reason is to avoid unexpected changes in formatting.
-    if ( $rOpts->{'extended-continuation-indentation'} ) {
+    if ($rOpts_extended_continuation_indentation) {
         while ( my ( $qw_seqno, $rKrange ) =
             each %{$rKrange_multiline_qw_by_seqno} )
         {
@@ -9301,7 +10671,8 @@ EOM
             my $token_beg = $rLL->[$Kbeg]->[_TOKEN_];
 
             # allow space(s) after the qw
-            if ( length($token_beg) > 3 && substr( $token_beg, 2, 1 ) eq ' ' ) {
+            if ( length($token_beg) > 3 && substr( $token_beg, 2, 1 ) =~ m/\s/ )
+            {
                 $token_beg =~ s/\s+//;
             }
 
@@ -9319,7 +10690,7 @@ EOM
 
     # For the -lp option we need to mark all parent containers of
     # multiline quotes
-    if ($rOpts_line_up_parentheses) {
+    if ( $rOpts_line_up_parentheses && !$rOpts_extended_line_up_parentheses ) {
 
         while ( my ( $qw_seqno, $rKrange ) =
             each %{$rKrange_multiline_qw_by_seqno} )
     return;
 }
 
-sub is_excluded_lp {
+use constant DEBUG_COLLAPSED_LENGTHS => 0;
 
-    # decide if this container is excluded by user request
-    # returns true if this token is excluded (i.e., may not use -lp)
-    # returns false otherwise
+# Minimum space reserved for contents of a code block.  A value of 40 has given
+# reasonable results.  With a large line length, say -l=120, this will not
+# normally be noticable but it will prevent making a mess in some edge cases.
+use constant MIN_BLOCK_LEN => 40;
 
-    # note similarity with sub 'is_excluded_weld'
-    my ( $self, $KK ) = @_;
-    my $rLL         = $self->[_rLL_];
-    my $rtoken_vars = $rLL->[$KK];
-    my $token       = $rtoken_vars->[_TOKEN_];
-    my $rflags      = $line_up_parentheses_exclusion_rules{$token};
-    return 0 unless ( defined($rflags) );
-    my ( $flag1, $flag2 ) = @{$rflags};
+my %is_handle_type;
 
-    # There are two flags:
-    # flag1 excludes based on the preceding nonblank word
-    # flag2 excludes based on the contents of the container
-    return 0 unless ( defined($flag1) );
-    return 1 if $flag1 eq '*';
+BEGIN {
+    my @q = qw( w C U G i k => );
+    @is_handle_type{@q} = (1) x scalar(@q);
 
-    # Find the previous token
-    my ( $is_f, $is_k, $is_w );
-    my $Kp = $self->K_previous_nonblank($KK);
-    if ( defined($Kp) ) {
-        my $type_p = $rLL->[$Kp]->[_TYPE_];
-        my $seqno  = $rtoken_vars->[_TYPE_SEQUENCE_];
+    my $i = 0;
+    use constant {
+        _max_prong_len_         => $i++,
+        _handle_len_            => $i++,
+        _seqno_o_               => $i++,
+        _iline_o_               => $i++,
+        _K_o_                   => $i++,
+        _K_c_                   => $i++,
+        _interrupted_list_rule_ => $i++,
+    };
+}
 
-        # keyword?
-        $is_k = $type_p eq 'k';
+sub collapsed_lengths {
 
-        # function call?
-        $is_f = $self->[_ris_function_call_paren_]->{$seqno};
+    my $self = shift;
 
-        # either keyword or function call?
-        $is_w = $is_k || $is_f;
-    }
+    #----------------------------------------------------------------
+    # Define the collapsed lengths of containers for -xlp indentation
+    #----------------------------------------------------------------
 
-    # Check for exclusion based on flag1 and the previous token:
-    my $match;
-    if    ( $flag1 eq 'k' ) { $match = $is_k }
-    elsif ( $flag1 eq 'K' ) { $match = !$is_k }
-    elsif ( $flag1 eq 'f' ) { $match = $is_f }
-    elsif ( $flag1 eq 'F' ) { $match = !$is_f }
-    elsif ( $flag1 eq 'w' ) { $match = $is_w }
-    elsif ( $flag1 eq 'W' ) { $match = !$is_w }
-    return $match if ($match);
-
-    # Check for exclusion based on flag2 and the container contents
-    # Current options to filter on contents:
-    # 0 or blank: ignore container contents
-    # 1 exclude non-lists or lists with sublists
-    # 2 same as 1 but also exclude lists with code blocks
-
-    # Note:
-    # Containers with multiline-qw containers are automatically
-    # excluded so do not need to be checked.
-    if ($flag2) {
+    # We need an estimate of the minimum required line length starting at any
+    # opening container for the -xlp style. This is needed to avoid using too
+    # much indentation space for lower level containers and thereby running
+    # out of space for outer container tokens due to the maximum line length
+    # limit.
 
-        my $seqno = $rtoken_vars->[_TYPE_SEQUENCE_];
+    # The basic idea is that at each node in the tree we imagine that we have a
+    # fork with a handle and collapsable prongs:
+    #
+    #                            |------------
+    #                            |--------
+    #                ------------|-------
+    #                 handle     |------------
+    #                            |--------
+    #                              prongs
+    #
+    # Each prong has a minimum collapsed length. The collapsed length at a node
+    # is the maximum of these minimum lengths, plus the handle length.  Each of
+    # the prongs may itself be a tree node.
 
-        my $is_list        = $self->[_ris_list_by_seqno_]->{$seqno};
-        my $has_list       = $self->[_rhas_list_]->{$seqno};
-        my $has_code_block = $self->[_rhas_code_block_]->{$seqno};
-        my $has_ternary    = $self->[_rhas_ternary_]->{$seqno};
-        if (  !$is_list
-            || $has_list
-            || $flag2 eq '2' && ( $has_code_block || $has_ternary ) )
-        {
-            $match = 1;
-        }
-    }
-    return $match;
-}
+    # This is just a rough calculation to get an approximate starting point for
+    # indentation.  Later routines will be more precise.  It is important that
+    # these estimates be independent of the line breaks of the input stream in
+    # order to avoid instabilities.
 
-sub set_excluded_lp_containers {
+    my $rLL                        = $self->[_rLL_];
+    my $Klimit                     = $self->[_Klimit_];
+    my $rlines                     = $self->[_rlines_];
+    my $K_opening_container        = $self->[_K_opening_container_];
+    my $K_closing_container        = $self->[_K_closing_container_];
+    my $rblock_type_of_seqno       = $self->[_rblock_type_of_seqno_];
+    my $rcollapsed_length_by_seqno = $self->[_rcollapsed_length_by_seqno_];
+    my $ris_excluded_lp_container  = $self->[_ris_excluded_lp_container_];
+    my $ris_permanently_broken     = $self->[_ris_permanently_broken_];
+    my $ris_list_by_seqno          = $self->[_ris_list_by_seqno_];
+    my $rhas_broken_list           = $self->[_rhas_broken_list_];
 
-    my ($self) = @_;
-    return unless ($rOpts_line_up_parentheses);
-    my $rLL = $self->[_rLL_];
-    return unless ( defined($rLL) && @{$rLL} );
+    my $K_start_multiline_qw;
+    my $level_start_multiline_qw = 0;
+    my $max_prong_len            = 0;
+    my $handle_len               = 0;
+    my @stack;
+    my $len                = 0;
+    my $last_nonblank_type = 'b';
+    push @stack,
+      [ $max_prong_len, $handle_len, SEQ_ROOT, undef, undef, undef, undef ];
 
-    my $K_opening_container       = $self->[_K_opening_container_];
-    my $ris_excluded_lp_container = $self->[_ris_excluded_lp_container_];
+    my $iline = -1;
+    foreach my $line_of_tokens ( @{$rlines} ) {
+        $iline++;
+        my $line_type = $line_of_tokens->{_line_type};
+        next if ( $line_type ne 'CODE' );
+        my $CODE_type = $line_of_tokens->{_code_type};
 
-    foreach my $seqno ( keys %{$K_opening_container} ) {
-        my $KK = $K_opening_container->{$seqno};
-        next unless defined($KK);
+        # Always skip blank lines
+        next if ( $CODE_type eq 'BL' );
 
-        # code blocks are always excluded by the -lp coding so we can skip them
-        next if ( $rLL->[$KK]->[_BLOCK_TYPE_] );
+        # Note on other line types:
+        # 'FS' (Format Skipping) lines may contain opening/closing tokens so
+        #      we have to process them to keep the stack correctly sequenced.
+        # 'VB' (Verbatim) lines could be skipped, but testing shows that
+        #      results look better if we include their lengths.
 
-        # see if a user exclusion rule turns off -lp for this container
-        if ( $self->is_excluded_lp($KK) ) {
-            $ris_excluded_lp_container->{$seqno} = 1;
-        }
-    }
-    return;
-}
+        # Also note that we could exclude -xlp formatting of containers with
+        # 'FS' and 'VB' lines, but in testing that was not really beneficial.
 
-######################################
-# CODE SECTION 6: Process line-by-line
-######################################
+        # So we process tokens in 'FS' and 'VB' lines like all the rest...
 
-sub process_all_lines {
+        my $rK_range = $line_of_tokens->{_rK_range};
+        my ( $K_first, $K_last ) = @{$rK_range};
+        next unless ( defined($K_first) && defined($K_last) );
+
+        my $has_comment = $rLL->[$K_last]->[_TYPE_] eq '#';
+
+        # Always ignore block comments
+        next if ( $has_comment && $K_first == $K_last );
+
+        # Handle an intermediate line of a multiline qw quote. These may
+        # require including some -ci or -i spaces.  See cases c098/x063.
+        # Updated to check all lines (not just $K_first==$K_last) to fix b1316
+        my $K_begin_loop = $K_first;
+        if ( $rLL->[$K_first]->[_TYPE_] eq 'q' ) {
+
+            my $KK       = $K_first;
+            my $level    = $rLL->[$KK]->[_LEVEL_];
+            my $ci_level = $rLL->[$KK]->[_CI_LEVEL_];
+
+            # remember the level of the start
+            if ( !defined($K_start_multiline_qw) ) {
+                $K_start_multiline_qw     = $K_first;
+                $level_start_multiline_qw = $level;
+                my $seqno_qw =
+                  $self->[_rstarting_multiline_qw_seqno_by_K_]
+                  ->{$K_start_multiline_qw};
+                if ( !$seqno_qw ) {
+                    my $Kp = $self->K_previous_nonblank($K_first);
+                    if ( defined($Kp) && $rLL->[$Kp]->[_TYPE_] eq 'q' ) {
+
+                        $K_start_multiline_qw = $Kp;
+                        $level_start_multiline_qw =
+                          $rLL->[$K_start_multiline_qw]->[_LEVEL_];
+                    }
+                }
+            }
 
-    # Main loop over all lines of a file.
-    # Lines are processed according to type.
+            $len = $rLL->[$KK]->[_CUMULATIVE_LENGTH_] -
+              $rLL->[ $KK - 1 ]->[_CUMULATIVE_LENGTH_];
 
-    my $self                       = shift;
-    my $rlines                     = $self->[_rlines_];
-    my $sink_object                = $self->[_sink_object_];
-    my $fh_tee                     = $self->[_fh_tee_];
-    my $rOpts_keep_old_blank_lines = $rOpts->{'keep-old-blank-lines'};
-    my $file_writer_object         = $self->[_file_writer_object_];
-    my $logger_object              = $self->[_logger_object_];
-    my $vertical_aligner_object    = $self->[_vertical_aligner_object_];
-    my $save_logfile               = $self->[_save_logfile_];
+            # We may have to add the spaces of one level or ci level ...  it
+            # depends depends on the -xci flag, the -wn flag, and if the qw
+            # uses a container token as the quote delimiter.
 
-    # Note for RT#118553, leave only one newline at the end of a file.
-    # Example code to do this is in comments below:
-    # my $Opt_trim_ending_blank_lines = 0;
-    # if ($Opt_trim_ending_blank_lines) {
-    #     while ( my $line_of_tokens = pop @{$rlines} ) {
-    #         my $line_type = $line_of_tokens->{_line_type};
-    #         if ( $line_type eq 'CODE' ) {
-    #             my $CODE_type = $line_of_tokens->{_code_type};
-    #             next if ( $CODE_type eq 'BL' );
-    #         }
-    #         push @{$rlines}, $line_of_tokens;
-    #         last;
-    #     }
-    # }
+            # First rule: add ci if there is a $ci_level
+            if ($ci_level) {
+                $len += $rOpts_continuation_indentation;
+            }
 
-   # But while this would be a trivial update, it would have very undesirable
-   # side effects when perltidy is run from within an editor on a small snippet.
-   # So this is best done with a separate filter, such
-   # as 'delete_ending_blank_lines.pl' in the examples folder.
+            # Second rule: otherwise, look for an extra indentation level
+            # from the start and add one indentation level if found.
+            elsif ( $level > $level_start_multiline_qw ) {
+                $len += $rOpts_indent_columns;
+            }
 
-    # Flag to prevent blank lines when POD occurs in a format skipping sect.
-    my $in_format_skipping_section;
+            if ( $len > $max_prong_len ) { $max_prong_len = $len }
 
-    # set locations for blanks around long runs of keywords
-    my $rwant_blank_line_after = $self->keyword_group_scan();
+            $last_nonblank_type = 'q';
 
-    my $line_type      = "";
-    my $i_last_POD_END = -10;
-    my $i              = -1;
-    foreach my $line_of_tokens ( @{$rlines} ) {
-        $i++;
+            $K_begin_loop = $K_first + 1;
+
+            # We can skip to the next line if more tokens
+            next if ( $K_begin_loop > $K_last );
 
-        # insert blank lines requested for keyword sequences
-        if (   $i > 0
-            && defined( $rwant_blank_line_after->{ $i - 1 } )
-            && $rwant_blank_line_after->{ $i - 1 } == 1 )
-        {
-            $self->want_blank_line();
+        }
+        $K_start_multiline_qw = undef;
+
+        # Find the terminal token, before any side comment
+        my $K_terminal = $K_last;
+        if ($has_comment) {
+            $K_terminal -= 1;
+            $K_terminal -= 1
+              if ( $rLL->[$K_terminal]->[_TYPE_] eq 'b'
+                && $K_terminal > $K_first );
+        }
+
+        # Use length to terminal comma if interrupded list rule applies
+        if ( @stack && $stack[-1]->[_interrupted_list_rule_] ) {
+            my $K_c = $stack[-1]->[_K_c_];
+            if (
+                defined($K_c)
+                && $rLL->[$K_terminal]->[_TYPE_] eq ','
+
+                # Ignore a terminal comma, causes instability (b1297)
+                && (   $K_c - $K_terminal > 2
+                    || $rLL->[ $K_terminal + 1 ]->[_TYPE_] eq 'b' )
+              )
+            {
+                my $Kend = $K_terminal;
+
+                # This caused an instability in b1311 by making the result
+                # dependent on input.  It is not really necessary because the
+                # comment length is added at the end of the loop.
+                ##if ( $has_comment
+                ##    && !$rOpts_ignore_side_comment_lengths )
+                ##{
+                ##    $Kend = $K_last;
+                ##}
+
+                $len = $rLL->[$Kend]->[_CUMULATIVE_LENGTH_] -
+                  $rLL->[ $K_first - 1 ]->[_CUMULATIVE_LENGTH_];
+
+                if ( $len > $max_prong_len ) { $max_prong_len = $len }
+            }
+        }
+
+        # Loop over tokens on this line ...
+        foreach my $KK ( $K_begin_loop .. $K_terminal ) {
+
+            my $type = $rLL->[$KK]->[_TYPE_];
+            next if ( $type eq 'b' );
+
+            #------------------------
+            # Handle sequenced tokens
+            #------------------------
+            my $seqno = $rLL->[$KK]->[_TYPE_SEQUENCE_];
+            if ($seqno) {
+
+                my $token = $rLL->[$KK]->[_TOKEN_];
+
+                #----------------------------
+                # Entering a new container...
+                #----------------------------
+                if ( $is_opening_token{$token} ) {
+
+                    # save current prong length
+                    $stack[-1]->[_max_prong_len_] = $max_prong_len;
+                    $max_prong_len = 0;
+
+                    # Start new prong one level deeper
+                    my $handle_len = 0;
+                    if ( $rblock_type_of_seqno->{$seqno} ) {
+
+                        # code blocks do not use -lp indentation, but behave as
+                        # if they had a handle of one indentation length
+                        $handle_len = $rOpts_indent_columns;
+
+                    }
+                    elsif ( $is_handle_type{$last_nonblank_type} ) {
+                        $handle_len = $len;
+                        $handle_len += 1
+                          if ( $KK > 0 && $rLL->[ $KK - 1 ]->[_TYPE_] eq 'b' );
+                    }
+
+                    # Set a flag if the 'Interrupted List Rule' will be applied
+                    # (see sub copy_old_breakpoints).
+                    # - Added check on has_broken_list to fix issue b1298
+
+                    my $interrupted_list_rule =
+                         $ris_permanently_broken->{$seqno}
+                      && $ris_list_by_seqno->{$seqno}
+                      && !$rhas_broken_list->{$seqno}
+                      && !$rOpts_ignore_old_breakpoints;
+
+                    # NOTES: Since we are looking at old line numbers we have
+                    # to be very careful not to introduce an instability.
+
+                    # This following causes instability (b1288-b1296):
+                    #   $interrupted_list_rule ||=
+                    #     $rOpts_break_at_old_comma_breakpoints;
+
+                    #  - We could turn off the interrupted list rule if there is
+                    #    a broken sublist, to follow 'Compound List Rule 1'.
+                    #  - We could use the _rhas_broken_list_ flag for this.
+                    #  - But it seems safer not to do this, to avoid
+                    #    instability, since the broken sublist could be
+                    #    temporary.  It seems better to let the formatting
+                    #    stabilize by itself after one or two iterations.
+                    #  - So, not doing this for now
+
+                    # Include length to a comma ending this line
+                    if (   $interrupted_list_rule
+                        && $rLL->[$K_terminal]->[_TYPE_] eq ',' )
+                    {
+                        my $Kend = $K_terminal;
+                        if ( $Kend < $K_last
+                            && !$rOpts_ignore_side_comment_lengths )
+                        {
+                            $Kend = $K_last;
+                        }
+
+                        # Measure from the next blank if any (fixes b1301)
+                        my $Kbeg = $KK;
+                        if (   $rLL->[ $Kbeg + 1 ]->[_TYPE_] eq 'b'
+                            && $Kbeg < $Kend )
+                        {
+                            $Kbeg++;
+                        }
+
+                        my $len = $rLL->[$Kend]->[_CUMULATIVE_LENGTH_] -
+                          $rLL->[$Kbeg]->[_CUMULATIVE_LENGTH_];
+                        if ( $len > $max_prong_len ) { $max_prong_len = $len }
+                    }
+
+                    my $K_c = $K_closing_container->{$seqno};
+
+                    push @stack,
+                      [
+                        $max_prong_len, $handle_len,
+                        $seqno,         $iline,
+                        $KK,            $K_c,
+                        $interrupted_list_rule
+                      ];
+                }
+
+                #--------------------
+                # Exiting a container
+                #--------------------
+                elsif ( $is_closing_token{$token} ) {
+                    if (@stack) {
+
+                        # The current prong ends - get its handle
+                        my $item          = pop @stack;
+                        my $handle_len    = $item->[_handle_len_];
+                        my $seqno_o       = $item->[_seqno_o_];
+                        my $iline_o       = $item->[_iline_o_];
+                        my $K_o           = $item->[_K_o_];
+                        my $K_c_expect    = $item->[_K_c_];
+                        my $collapsed_len = $max_prong_len;
+
+                        if ( $seqno_o ne $seqno ) {
+
+                            # Shouldn't happen - must have skipped some lines.
+                            # Not fatal but -lp formatting could get messed up.
+                            if (DEVEL_MODE) {
+                                Fault(<<EOM);
+sequence numbers differ; at CLOSING line $iline, seq=$seqno, Kc=$KK .. at OPENING line $iline_o, seq=$seqno_o, Ko=$K_o, expecting Kc=$K_c_expect
+EOM
+                            }
+                        }
+
+                        #------------------------------------------
+                        # Rules to avoid scrunching code blocks ...
+                        #------------------------------------------
+                        # Some test cases:
+                        # c098/x107 x108 x110 x112 x114 x115 x117 x118 x119
+                        if ( $rblock_type_of_seqno->{$seqno} ) {
+
+                            my $K_c          = $KK;
+                            my $block_length = MIN_BLOCK_LEN;
+                            my $is_one_line_block;
+                            my $level = $rLL->[$K_o]->[_LEVEL_];
+                            if ( defined($K_o) && defined($K_c) ) {
+                                my $block_length =
+                                  $rLL->[ $K_c - 1 ]->[_CUMULATIVE_LENGTH_] -
+                                  $rLL->[$K_o]->[_CUMULATIVE_LENGTH_];
+                                $is_one_line_block = $iline == $iline_o;
+                            }
+
+                            # Code block rule 1: Use the total block length if
+                            # it is less than the minimum.
+                            if ( $block_length < MIN_BLOCK_LEN ) {
+                                $collapsed_len = $block_length;
+                            }
+
+                            # Code block rule 2: Use the full length of a
+                            # one-line block to avoid breaking it, unless
+                            # extremely long.  We do not need to do a precise
+                            # check here, because if it breaks then it will
+                            # stay broken on later iterations.
+                            elsif ($is_one_line_block
+                                && $block_length <
+                                $maximum_line_length_at_level[$level] )
+                            {
+                                $collapsed_len = $block_length;
+                            }
+
+                            # Code block rule 3: Otherwise the length should be
+                            # at least MIN_BLOCK_LEN to avoid scrunching code
+                            # blocks.
+                            elsif ( $collapsed_len < MIN_BLOCK_LEN ) {
+                                $collapsed_len = MIN_BLOCK_LEN;
+                            }
+                        }
+
+                        # Store the result.  Some extra space, '2', allows for
+                        # length of an opening token, inside space, comma, ...
+                        # This constant has been tuned to give good overall
+                        # results.
+                        $collapsed_len += 2;
+                        $rcollapsed_length_by_seqno->{$seqno} = $collapsed_len;
+
+                        # Restart scanning the lower level prong
+                        if (@stack) {
+                            $max_prong_len = $stack[-1]->[_max_prong_len_];
+                            $collapsed_len += $handle_len;
+                            if ( $collapsed_len > $max_prong_len ) {
+                                $max_prong_len = $collapsed_len;
+                            }
+                        }
+                    }
+                }
+
+                # it is a ternary - no special processing for these yet
+                else {
+
+                }
+
+                $len                = 0;
+                $last_nonblank_type = $type;
+                next;
+            }
+
+            #----------------------------
+            # Handle non-container tokens
+            #----------------------------
+            my $token_length = $rLL->[$KK]->[_TOKEN_LENGTH_];
+
+            # Count lengths of things like 'xx => yy' as a single item
+            if ( $type eq '=>' ) {
+                $len += $token_length + 1;
+                if ( $len > $max_prong_len ) { $max_prong_len = $len }
+            }
+            elsif ( $last_nonblank_type eq '=>' ) {
+                $len += $token_length;
+                if ( $len > $max_prong_len ) { $max_prong_len = $len }
+
+                # but only include one => per item
+                if ( $last_nonblank_type eq '=>' ) { $len = $token_length }
+            }
+
+            # include everthing to end of line after a here target
+            elsif ( $type eq 'h' ) {
+                $len = $rLL->[$K_last]->[_CUMULATIVE_LENGTH_] -
+                  $rLL->[ $KK - 1 ]->[_CUMULATIVE_LENGTH_];
+                if ( $len > $max_prong_len ) { $max_prong_len = $len }
+            }
+
+            # for everything else just use the token length
+            else {
+                $len = $token_length;
+                if ( $len > $max_prong_len ) { $max_prong_len = $len }
+            }
+            $last_nonblank_type = $type;
+
+        } ## end loop over tokens on this line
+
+        # Now take care of any side comment
+        if ($has_comment) {
+            if ($rOpts_ignore_side_comment_lengths) {
+                $len = 0;
+            }
+            else {
+
+                # For a side comment when -iscl is not set, measure length from
+                # the start of the previous nonblank token
+                my $len0 =
+                    $K_terminal > 0
+                  ? $rLL->[ $K_terminal - 1 ]->[_CUMULATIVE_LENGTH_]
+                  : 0;
+                $len = $rLL->[$K_last]->[_CUMULATIVE_LENGTH_] - $len0;
+                if ( $len > $max_prong_len ) { $max_prong_len = $len }
+            }
+        }
+
+    } ## end loop over lines
+
+    if (DEBUG_COLLAPSED_LENGTHS) {
+        print "\nCollapsed lengths--\n";
+        foreach
+          my $key ( sort { $a <=> $b } keys %{$rcollapsed_length_by_seqno} )
+        {
+            my $clen = $rcollapsed_length_by_seqno->{$key};
+            print "$key -> $clen\n";
+        }
+    }
+
+    return;
+}
+
+sub is_excluded_lp {
+
+    # Decide if this container is excluded by user request:
+    #  returns true if this token is excluded (i.e., may not use -lp)
+    #  returns false otherwise
+
+    # The control hash can either describe:
+    #   what to exclude:  $line_up_parentheses_control_is_lxpl = 1, or
+    #   what to include:  $line_up_parentheses_control_is_lxpl = 0
+
+    my ( $self, $KK ) = @_;
+    my $rLL         = $self->[_rLL_];
+    my $rtoken_vars = $rLL->[$KK];
+    my $token       = $rtoken_vars->[_TOKEN_];
+    my $rflags      = $line_up_parentheses_control_hash{$token};
+
+    #-----------------------------------------------
+    # TEST #1: check match to listed container types
+    #-----------------------------------------------
+    if ( !defined($rflags) ) {
+
+        # There is no entry for this container, so we are done
+        return !$line_up_parentheses_control_is_lxpl;
+    }
+
+    my ( $flag1, $flag2 ) = @{$rflags};
+
+    #-----------------------------------------------------------
+    # TEST #2: check match to flag1, the preceding nonblank word
+    #-----------------------------------------------------------
+    my $match_flag1 = !defined($flag1) || $flag1 eq '*';
+    if ( !$match_flag1 ) {
+
+        # Find the previous token
+        my ( $is_f, $is_k, $is_w );
+        my $Kp = $self->K_previous_nonblank($KK);
+        if ( defined($Kp) ) {
+            my $type_p = $rLL->[$Kp]->[_TYPE_];
+            my $seqno  = $rtoken_vars->[_TYPE_SEQUENCE_];
+
+            # keyword?
+            $is_k = $type_p eq 'k';
+
+            # function call?
+            $is_f = $self->[_ris_function_call_paren_]->{$seqno};
+
+            # either keyword or function call?
+            $is_w = $is_k || $is_f;
+        }
+
+        # Check for match based on flag1 and the previous token:
+        if    ( $flag1 eq 'k' ) { $match_flag1 = $is_k }
+        elsif ( $flag1 eq 'K' ) { $match_flag1 = !$is_k }
+        elsif ( $flag1 eq 'f' ) { $match_flag1 = $is_f }
+        elsif ( $flag1 eq 'F' ) { $match_flag1 = !$is_f }
+        elsif ( $flag1 eq 'w' ) { $match_flag1 = $is_w }
+        elsif ( $flag1 eq 'W' ) { $match_flag1 = !$is_w }
+    }
+
+    # See if we can exclude this based on the flag1 test...
+    if ($line_up_parentheses_control_is_lxpl) {
+        return 1 if ($match_flag1);
+    }
+    else {
+        return 1 if ( !$match_flag1 );
+    }
+
+    #-------------------------------------------------------------
+    # TEST #3: exclusion based on flag2 and the container contents
+    #-------------------------------------------------------------
+
+    # Note that this is an exclusion test for both -lpxl or -lpil input methods
+    # The options are:
+    #  0 or blank: ignore container contents
+    #  1 exclude non-lists or lists with sublists
+    #  2 same as 1 but also exclude lists with code blocks
+
+    my $match_flag2;
+    if ($flag2) {
+
+        my $seqno = $rtoken_vars->[_TYPE_SEQUENCE_];
+
+        my $is_list        = $self->[_ris_list_by_seqno_]->{$seqno};
+        my $has_list       = $self->[_rhas_list_]->{$seqno};
+        my $has_code_block = $self->[_rhas_code_block_]->{$seqno};
+        my $has_ternary    = $self->[_rhas_ternary_]->{$seqno};
+
+        if (  !$is_list
+            || $has_list
+            || $flag2 eq '2' && ( $has_code_block || $has_ternary ) )
+        {
+            $match_flag2 = 1;
+        }
+    }
+    return $match_flag2;
+}
+
+sub set_excluded_lp_containers {
+
+    my ($self) = @_;
+    return unless ($rOpts_line_up_parentheses);
+    my $rLL = $self->[_rLL_];
+    return unless ( defined($rLL) && @{$rLL} );
+
+    my $K_opening_container       = $self->[_K_opening_container_];
+    my $ris_excluded_lp_container = $self->[_ris_excluded_lp_container_];
+    my $rblock_type_of_seqno      = $self->[_rblock_type_of_seqno_];
+
+    foreach my $seqno ( keys %{$K_opening_container} ) {
+
+        # code blocks are always excluded by the -lp coding so we can skip them
+        next if ( $rblock_type_of_seqno->{$seqno} );
+
+        my $KK = $K_opening_container->{$seqno};
+        next unless defined($KK);
+
+        # see if a user exclusion rule turns off -lp for this container
+        if ( $self->is_excluded_lp($KK) ) {
+            $ris_excluded_lp_container->{$seqno} = 1;
+        }
+    }
+    return;
+}
+
+######################################
+# CODE SECTION 6: Process line-by-line
+######################################
+
+sub process_all_lines {
+
+    #----------------------------------------------------------
+    # Main loop to format all lines of a file according to type
+    #----------------------------------------------------------
+
+    my $self                       = shift;
+    my $rlines                     = $self->[_rlines_];
+    my $sink_object                = $self->[_sink_object_];
+    my $fh_tee                     = $self->[_fh_tee_];
+    my $rOpts_keep_old_blank_lines = $rOpts->{'keep-old-blank-lines'};
+    my $file_writer_object         = $self->[_file_writer_object_];
+    my $logger_object              = $self->[_logger_object_];
+    my $vertical_aligner_object    = $self->[_vertical_aligner_object_];
+    my $save_logfile               = $self->[_save_logfile_];
+
+    # Note for RT#118553, leave only one newline at the end of a file.
+    # Example code to do this is in comments below:
+    # my $Opt_trim_ending_blank_lines = 0;
+    # if ($Opt_trim_ending_blank_lines) {
+    #     while ( my $line_of_tokens = pop @{$rlines} ) {
+    #         my $line_type = $line_of_tokens->{_line_type};
+    #         if ( $line_type eq 'CODE' ) {
+    #             my $CODE_type = $line_of_tokens->{_code_type};
+    #             next if ( $CODE_type eq 'BL' );
+    #         }
+    #         push @{$rlines}, $line_of_tokens;
+    #         last;
+    #     }
+    # }
+
+   # But while this would be a trivial update, it would have very undesirable
+   # side effects when perltidy is run from within an editor on a small snippet.
+   # So this is best done with a separate filter, such
+   # as 'delete_ending_blank_lines.pl' in the examples folder.
+
+    # Flag to prevent blank lines when POD occurs in a format skipping sect.
+    my $in_format_skipping_section;
+
+    # set locations for blanks around long runs of keywords
+    my $rwant_blank_line_after = $self->keyword_group_scan();
+
+    my $line_type      = "";
+    my $i_last_POD_END = -10;
+    my $i              = -1;
+    foreach my $line_of_tokens ( @{$rlines} ) {
+        $i++;
+
+        # insert blank lines requested for keyword sequences
+        if (   $i > 0
+            && defined( $rwant_blank_line_after->{ $i - 1 } )
+            && $rwant_blank_line_after->{ $i - 1 } == 1 )
+        {
+            $self->want_blank_line();
         }
 
         my $last_line_type = $line_type;
@@ -9540,6 +11407,8 @@ sub process_all_lines {
         #   HERE_END       - last line of here-doc (target word)
         #   FORMAT         - format section
         #   FORMAT_END     - last line of format section, '.'
+        #   SKIP           - code skipping section
+        #   SKIP_END       - last line of code skipping section, '#>>V'
         #   DATA_START     - __DATA__ line
         #   DATA           - unidentified text following __DATA__
         #   END_START      - __END__ line
@@ -9597,8 +11466,8 @@ sub process_all_lines {
             }
             else {
 
-          # Let logger see all non-blank lines of code. This is a slow operation
-          # so we avoid it if it is not going to be saved.
+                # Let logger see all non-blank lines of code. This is a slow
+                # operation so we avoid it if it is not going to be saved.
                 if ( $save_logfile && $logger_object ) {
                     $logger_object->black_box( $line_of_tokens,
                         $vertical_aligner_object->get_output_line_number );
@@ -9644,6 +11513,12 @@ sub process_all_lines {
                 $self->[_saw_END_or_DATA_] = 1;
             }
 
+            # Patch to avoid losing blank lines after a code-skipping block;
+            # fixes case c047.
+            elsif ( $line_type eq 'SKIP_END' ) {
+                $file_writer_object->reset_consecutive_blank_lines();
+            }
+
             # write unindented non-code line
             if ( !$skip_line ) {
                 $self->write_unindented_line($input_line);
@@ -9657,7 +11532,9 @@ sub process_all_lines {
 sub keyword_group_scan {
     my $self = shift;
 
-    # Called once per file to process the --keyword-group-blanks-* parameters.
+    #-------------------------------------------------------------------------
+    # Called once per file to process any --keyword-group-blanks-* parameters.
+    #-------------------------------------------------------------------------
 
     # Manipulate blank lines around keyword groups (kgb* flags)
     # Scan all lines looking for runs of consecutive lines beginning with
@@ -9735,6 +11612,8 @@ EOM
     my $rlines              = $self->[_rlines_];
     my $rLL                 = $self->[_rLL_];
     my $K_closing_container = $self->[_K_closing_container_];
+    my $K_opening_container = $self->[_K_opening_container_];
+    my $rK_weld_right       = $self->[_rK_weld_right_];
 
     # variables for the current group and subgroups:
     my ( $ibeg, $iend, $count, $level_beg, $K_closing, @iblanks, @group,
@@ -9757,9 +11636,9 @@ EOM
 
     my $number_of_groups_seen = 0;
 
-    ####################
+    #-------------------
     # helper subroutines
-    ####################
+    #-------------------
 
     my $insert_blank_after = sub {
         my ($i) = @_;
@@ -9819,6 +11698,7 @@ EOM
                 }
             }
         }
+        return;
     };
 
     my $delete_if_blank = sub {
@@ -9847,6 +11727,7 @@ EOM
 
         while ( my $ibl = pop(@iblanks) ) { $rhash_of_desires->{$ibl} = 2 }
 
+        return;
     };
 
     my $end_group = sub {
@@ -9927,29 +11808,48 @@ EOM
         @group     = ();
         @subgroup  = ();
         @iblanks   = ();
+
+        return;
     };
 
     my $find_container_end = sub {
 
-        # If the keyword lines ends with an open token, find the closing token
-        # '$K_closing' so that we can easily skip past the contents of the
-        # container.
-        return if ( $K_last <= $K_first );
-        my $KK        = $K_last;
-        my $type_last = $rLL->[$KK]->[_TYPE_];
-        my $tok_last  = $rLL->[$KK]->[_TOKEN_];
-        if ( $type_last eq '#' ) {
-            $KK       = $self->K_previous_nonblank($KK);
-            $tok_last = $rLL->[$KK]->[_TOKEN_];
-        }
-        if ( $KK > $K_first && $tok_last =~ /^[\(\{\[]$/ ) {
+        # If the keyword line is continued onto subsequent lines, find the
+        # closing token '$K_closing' so that we can easily skip past the
+        # contents of the container.
 
-            my $type_sequence = $rLL->[$KK]->[_TYPE_SEQUENCE_];
-            my $lev           = $rLL->[$KK]->[_LEVEL_];
-            if ( $lev == $level_beg ) {
-                $K_closing = $K_closing_container->{$type_sequence};
-            }
-        }
+        # We only set this value if we find a simple list, meaning
+        # -contents only one level deep
+        # -not welded
+
+        # First check: skip if next line is not one deeper
+        my $Knext_nonblank = $self->K_next_nonblank($K_last);
+        goto RETURN if ( !defined($Knext_nonblank) );
+        my $level_next = $rLL->[$Knext_nonblank]->[_LEVEL_];
+        goto RETURN if ( $level_next != $level_beg + 1 );
+
+        # Find the parent container of the first token on the next line
+        my $parent_seqno = $self->parent_seqno_by_K($Knext_nonblank);
+        goto RETURN unless ( defined($parent_seqno) );
+
+        # Must not be a weld (can be unstable)
+        goto RETURN
+          if ( $total_weld_count && $self->is_welded_at_seqno($parent_seqno) );
+
+        # Opening container must exist and be on this line
+        my $Ko = $K_opening_container->{$parent_seqno};
+        goto RETURN unless ( defined($Ko) && $Ko > $K_first && $Ko <= $K_last );
+
+        # Verify that the closing container exists and is on a later line
+        my $Kc = $K_closing_container->{$parent_seqno};
+        goto RETURN unless ( defined($Kc) && $Kc > $K_last );
+
+        # That's it
+        $K_closing = $Kc;
+        goto RETURN;
+
+      RETURN:
+        return;
     };
 
     my $add_to_group = sub {
@@ -9982,9 +11882,9 @@ EOM
         return;
     };
 
-    ###################################
+    #----------------------------------
     # loop over all lines of the source
-    ###################################
+    #----------------------------------
     $end_group->();
     my $i = -1;
     foreach my $line_of_tokens ( @{$rlines} ) {
@@ -10042,15 +11942,36 @@ EOM
             return $rhash_of_desires;
         }
 
-        # This is not for keywords in lists ( keyword 'my' can occur in lists,
-        # see case b760)
-        next if ( $self->is_list_by_K($K_first) );
-
         my $level    = $rLL->[$K_first]->[_LEVEL_];
         my $type     = $rLL->[$K_first]->[_TYPE_];
         my $token    = $rLL->[$K_first]->[_TOKEN_];
         my $ci_level = $rLL->[$K_first]->[_CI_LEVEL_];
 
+        # End a group 'badly' at an unexpected level.  This will prevent
+        # blank lines being incorrectly placed after the end of the group.
+        # We are looking for any deviation from two acceptable patterns:
+        #   PATTERN 1: a simple list; secondary lines are at level+1
+        #   PATTERN 2: a long statement; all secondary lines same level
+        # This was added as a fix for case b1177, in which a complex structure
+        # got incorrectly inserted blank lines.
+        if ( $ibeg >= 0 ) {
+
+            # Check for deviation from PATTERN 1, simple list:
+            if ( defined($K_closing) && $K_first < $K_closing ) {
+                $end_group->(1) if ( $level != $level_beg + 1 );
+            }
+
+            # Check for deviation from PATTERN 2, single statement:
+            elsif ( $level != $level_beg ) { $end_group->(1) }
+        }
+
+        # Do not look for keywords in lists ( keyword 'my' can occur in lists,
+        # see case b760); fixed for c048.
+        if ( $self->is_list_by_K($K_first) ) {
+            if ( $ibeg >= 0 ) { $iend = $i }
+            next;
+        }
+
         # see if this is a code type we seek (i.e. comment)
         if (   $CODE_type
             && $Opt_comment_pattern
@@ -10069,7 +11990,7 @@ EOM
 
                 # first end old group if any; we might be starting new
                 # keywords at different level
-                if ( $ibeg > 0 ) { $end_group->(); }
+                if ( $ibeg >= 0 ) { $end_group->(); }
                 $add_to_group->( $i, $tok, $level );
             }
             next;
@@ -10092,7 +12013,7 @@ EOM
 
                 # first end old group if any; we might be starting new
                 # keywords at different level
-                if ( $ibeg > 0 ) { $end_group->(); }
+                if ( $ibeg >= 0 ) { $end_group->(); }
                 $add_to_group->( $i, $token, $level );
             }
             next;
@@ -10105,7 +12026,7 @@ EOM
             # - bail out on a large level change; we may have walked into a
             #   data structure or anoymous sub code.
             if ( $level > $level_beg + 1 || $level < $level_beg ) {
-                $end_group->();
+                $end_group->(1);
                 next;
             }
 
@@ -10177,37 +12098,32 @@ EOM
     # flags needed by the store routine
     my $line_of_tokens;
     my $no_internal_newlines;
-    my $side_comment_follows;
     my $CODE_type;
 
     # range of K of tokens for the current line
     my ( $K_first, $K_last );
 
-    my ( $rLL, $radjusted_levels );
+    my ( $rLL, $radjusted_levels, $rparent_of_seqno, $rdepth_of_opening_seqno,
+        $rblock_type_of_seqno, $ri_starting_one_line_block );
 
-    # past stored nonblank tokens
+    # past stored nonblank tokens and flags
     my (
-        $last_last_nonblank_token,  $last_last_nonblank_type,
-        $last_nonblank_token,       $last_nonblank_type,
-        $last_nonblank_block_type,  $K_last_nonblank_code,
-        $K_last_last_nonblank_code, $looking_for_else,
-        $is_static_block_comment,   $batch_CODE_type,
-        $last_line_had_side_comment,
+        $K_last_nonblank_code, $K_last_last_nonblank_code,
+        $looking_for_else,     $is_static_block_comment,
+        $batch_CODE_type,      $last_line_had_side_comment,
+        $next_parent_seqno,    $next_slevel,
     );
 
     # Called once at the start of a new file
     sub initialize_process_line_of_CODE {
-        $last_nonblank_token        = ';';
-        $last_nonblank_type         = ';';
-        $last_last_nonblank_token   = ';';
-        $last_last_nonblank_type    = ';';
-        $last_nonblank_block_type   = "";
         $K_last_nonblank_code       = undef;
         $K_last_last_nonblank_code  = undef;
         $looking_for_else           = 0;
         $is_static_block_comment    = 0;
         $batch_CODE_type            = "";
         $last_line_had_side_comment = 0;
+        $next_parent_seqno          = SEQ_ROOT;
+        $next_slevel                = undef;
         return;
     }
 
@@ -10221,8 +12137,9 @@ EOM
     # Called before the start of each new batch
     sub initialize_batch_variables {
 
-        $max_index_to_go      = UNDEFINED_INDEX;
-        @summed_lengths_to_go = @nesting_depth_to_go = (0);
+        $max_index_to_go            = UNDEFINED_INDEX;
+        @summed_lengths_to_go       = @nesting_depth_to_go = (0);
+        $ri_starting_one_line_block = [];
 
         # The initialization code for the remaining batch arrays is as follows
         # and can be activated for testing.  But profiling shows that it is
@@ -10265,7 +12182,7 @@ EOM
     sub leading_spaces_to_go {
 
         # return the number of indentation spaces for a token in the output
-        # stream; these were previously stored by 'set_leading_whitespace'.
+        # stream
 
         my ($ii) = @_;
         return 0 if ( $ii < 0 );
@@ -10295,16 +12212,9 @@ EOM
         my ( $self, $Ktoken_vars, $rtoken_vars ) = @_;
 
         # Add one token to the next batch.
-        # $Ktoken_vars = the index K in the global token array
-        # $rtoken_vars = $rLL->[$Ktoken_vars] = the corresponding token values
-        #                unless they are temporarily being overridden
-
-        # NOTE: This routine needs to be coded efficiently because it is called
-        # once per token.  I have gotten it down from the second slowest to the
-        # eighth slowest, but that still seems rather slow for what it does.
-
-        # This closure variable has already been defined, for efficiency:
-        #     my $radjusted_levels = $self->[_radjusted_levels_];
+        #   $Ktoken_vars = the index K in the global token array
+        #   $rtoken_vars = $rLL->[$Ktoken_vars] = the corresponding token values
+        #                  unless they are temporarily being overridden
 
         my $type = $rtoken_vars->[_TYPE_];
 
@@ -10328,6 +12238,14 @@ EOM
             # happen, but it is worth checking.  Later code can then make the
             # simplifying assumption that blank tokens are not consecutive.
             elsif ( $type eq 'b' && $types_to_go[$max_index_to_go] eq 'b' ) {
+
+                if (DEVEL_MODE) {
+
+                    # if this happens, it is may be that consecutive blanks
+                    # were inserted into the token stream in 'respace_tokens'
+                    my $lno = $rLL->[$Ktoken_vars]->[_LINE_INDEX_] + 1;
+                    Fault("consecutive blanks near line $lno; please fix");
+                }
                 return;
             }
         }
@@ -10357,21 +12275,89 @@ EOM
         if ( $level < 0 ) { $level = 0 }
         $levels_to_go[$max_index_to_go] = $level;
 
-        $nesting_depth_to_go[$max_index_to_go] = $rtoken_vars->[_SLEVEL_];
-        $block_type_to_go[$max_index_to_go]    = $rtoken_vars->[_BLOCK_TYPE_];
-        $type_sequence_to_go[$max_index_to_go] =
+        my $seqno = $type_sequence_to_go[$max_index_to_go] =
           $rtoken_vars->[_TYPE_SEQUENCE_];
 
-        $nobreak_to_go[$max_index_to_go] =
-          $side_comment_follows ? 2 : $no_internal_newlines;
+        if ( $max_index_to_go == 0 ) {
 
-        my $length = $rtoken_vars->[_TOKEN_LENGTH_];
+            # Update the next parent sequence number for each new batch.
 
-        # Safety check that length is defined. Should not be needed now.
-        # Former patch for indent-only, in which the entire set of tokens is
-        # turned into type 'q'. Lengths may have not been defined because sub
-        # 'respace_tokens' is bypassed. We do not need lengths in this case,
-        # but we will use the character count to have a defined value.  In the
+            #------------------------------------------
+            # Begin coding from sub parent_seqno_from_K
+            #------------------------------------------
+
+            ## $next_parent_seqno = $self->parent_seqno_by_K($Ktoken_vars);
+            $next_parent_seqno = SEQ_ROOT;
+            if ($seqno) {
+                $next_parent_seqno = $rparent_of_seqno->{$seqno};
+            }
+            else {
+                my $Kt = $rLL->[$Ktoken_vars]->[_KNEXT_SEQ_ITEM_];
+                if ( defined($Kt) ) {
+                    my $type_sequence = $rLL->[$Kt]->[_TYPE_SEQUENCE_];
+                    my $type          = $rLL->[$Kt]->[_TYPE_];
+
+                    # if next container token is closing, it is the parent seqno
+                    if ( $is_closing_type{$type} ) {
+                        $next_parent_seqno = $type_sequence;
+                    }
+
+                    # otherwise we want its parent container
+                    else {
+                        $next_parent_seqno =
+                          $rparent_of_seqno->{$type_sequence};
+                    }
+                }
+            }
+            $next_parent_seqno = SEQ_ROOT
+              unless ( defined($next_parent_seqno) );
+
+            #----------------------------------------
+            # End coding from sub parent_seqno_from_K
+            #----------------------------------------
+
+            $next_slevel = $rdepth_of_opening_seqno->[$next_parent_seqno] + 1;
+        }
+
+        # Initialize some sequence-dependent variables to their normal values
+        my $parent_seqno = $next_parent_seqno;
+        my $slevel       = $next_slevel;
+        my $block_type   = "";
+
+        # Then fix them at container tokens:
+        if ($seqno) {
+            if ( $is_opening_token{$token} ) {
+                $next_parent_seqno = $seqno;
+                $slevel            = $rdepth_of_opening_seqno->[$seqno];
+                $next_slevel       = $slevel + 1;
+                $block_type        = $rblock_type_of_seqno->{$seqno};
+            }
+            elsif ( $is_closing_token{$token} ) {
+                $next_slevel       = $rdepth_of_opening_seqno->[$seqno];
+                $slevel            = $next_slevel + 1;
+                $block_type        = $rblock_type_of_seqno->{$seqno};
+                $parent_seqno      = $rparent_of_seqno->{$seqno};
+                $parent_seqno      = SEQ_ROOT unless defined($parent_seqno);
+                $next_parent_seqno = $parent_seqno;
+            }
+            else {
+                # ternary token: nothing to do
+            }
+            $block_type = "" unless ( defined($block_type) );
+        }
+
+        $parent_seqno_to_go[$max_index_to_go]  = $parent_seqno;
+        $nesting_depth_to_go[$max_index_to_go] = $slevel;
+        $block_type_to_go[$max_index_to_go]    = $block_type;
+        $nobreak_to_go[$max_index_to_go]       = $no_internal_newlines;
+
+        my $length = $rtoken_vars->[_TOKEN_LENGTH_];
+
+        # Safety check that length is defined. Should not be needed now.
+        # Former patch for indent-only, in which the entire set of tokens is
+        # turned into type 'q'. Lengths may have not been defined because sub
+        # 'respace_tokens' is bypassed. We do not need lengths in this case,
+        # but we will use the character count to have a defined value.  In the
         # future, it would be nicer to have 'respace_tokens' convert the lines
         # to quotes and get correct lengths.
         if ( !defined($length) ) { $length = length($token) }
@@ -10403,13 +12389,8 @@ EOM
             $leading_spaces_to_go[$max_index_to_go] =
               $reduced_spaces + $rOpts_continuation_indentation * $ci_level;
         }
-
-        # Correct these values if -lp is used
-        if ($rOpts_line_up_parentheses) {
-            $self->set_leading_whitespace( $Ktoken_vars, $K_last_nonblank_code,
-                $K_last_last_nonblank_code, $level, $ci_level,
-                $in_continued_quote );
-        }
+        $standard_spaces_to_go[$max_index_to_go] =
+          $leading_spaces_to_go[$max_index_to_go];
 
         DEBUG_STORE && do {
             my ( $a, $b, $c ) = caller();
@@ -10432,7 +12413,6 @@ EOM
         $this_batch->[_starting_in_quote_] = $starting_in_quote;
         $this_batch->[_ending_in_quote_]   = $ending_in_quote;
         $this_batch->[_max_index_to_go_]   = $max_index_to_go;
-        $this_batch->[_rK_to_go_]          = \@K_to_go;
         $this_batch->[_batch_CODE_type_]   = $batch_CODE_type;
 
         # The flag $is_static_block_comment applies to the line which just
@@ -10442,6 +12422,9 @@ EOM
           && $max_index_to_go == 0
           && $K_to_go[0] == $K_first ? $is_static_block_comment : 0;
 
+        $this_batch->[_ri_starting_one_line_block_] =
+          $ri_starting_one_line_block;
+
         $self->[_this_batch_] = $this_batch;
 
         $last_line_had_side_comment =
@@ -10454,8 +12437,6 @@ EOM
 
         initialize_batch_variables();
         initialize_forced_breakpoint_vars();
-        initialize_gnu_batch_vars()
-          if $rOpts_line_up_parentheses;
 
         return;
     }
@@ -10465,18 +12446,31 @@ EOM
         # end the current batch, EXCEPT for a few special cases
         my ($self) = @_;
 
-        # Exception 1: Do not end line in a weld
-        return
-          if ( $total_weld_count
-            && $self->is_welded_right_at_i($max_index_to_go) );
+        if ( $max_index_to_go < 0 ) {
 
-        # Exception 2: just set a tentative breakpoint if we might be in a
-        # one-line block
-        if ( $index_start_one_line_block != UNDEFINED_INDEX ) {
-            $self->set_forced_breakpoint($max_index_to_go);
+            # This is harmless but should be elimintated in development
+            if (DEVEL_MODE) {
+                Fault("End batch called with nothing to do; please fix\n");
+            }
             return;
         }
 
+        # Exceptions when a line does not end with a comment... (fixes c058)
+        if ( $types_to_go[$max_index_to_go] ne '#' ) {
+
+            # Exception 1: Do not end line in a weld
+            return
+              if ( $total_weld_count
+                && $self->[_rK_weld_right_]->{ $K_to_go[$max_index_to_go] } );
+
+            # Exception 2: just set a tentative breakpoint if we might be in a
+            # one-line block
+            if ( $index_start_one_line_block != UNDEFINED_INDEX ) {
+                $self->set_forced_breakpoint($max_index_to_go);
+                return;
+            }
+        }
+
         $self->flush_batch_of_CODE();
         return;
     }
@@ -10500,7 +12494,9 @@ EOM
         # Exception: if we are flushing within the code stream only to insert
         # blank line(s), then we can keep the batch intact at a weld. This
         # improves formatting of -ce.  See test 'ce1.ce'
-        if ( $CODE_type && $CODE_type eq 'BL' ) { $self->end_batch() }
+        if ( $CODE_type && $CODE_type eq 'BL' ) {
+            $self->end_batch() if ( $max_index_to_go >= 0 );
+        }
 
         # otherwise, we have to shut things down completely.
         else { $self->flush_batch_of_CODE() }
@@ -10513,8 +12509,10 @@ EOM
 
         my ( $self, $my_line_of_tokens ) = @_;
 
-        # This routine is called once per INPUT line to process all of the
+        #----------------------------------------------------------------
+        # This routine is called once per INPUT line to format all of the
         # tokens on that line.
+        #----------------------------------------------------------------
 
         # It outputs full-line comments and blank lines immediately.
 
@@ -10538,42 +12536,38 @@ EOM
         # appropriate for lists and logical structures, and to keep line
         # lengths below the requested maximum line length.
 
+        #-----------------------------------
+        # begin initialize closure variables
+        #-----------------------------------
         $line_of_tokens = $my_line_of_tokens;
         $CODE_type      = $line_of_tokens->{_code_type};
-        my $input_line_number = $line_of_tokens->{_line_number};
-        my $input_line        = $line_of_tokens->{_line_text};
-
-        # initialize closure variables
         my $rK_range = $line_of_tokens->{_rK_range};
         ( $K_first, $K_last ) = @{$rK_range};
-
-        # remember original starting index in case it changes
-        my $K_first_true = $K_first;
-
-        $rLL              = $self->[_rLL_];
-        $radjusted_levels = $self->[_radjusted_levels_];
-
-        my $file_writer_object = $self->[_file_writer_object_];
-        my $rbreak_container   = $self->[_rbreak_container_];
-        my $rshort_nested      = $self->[_rshort_nested_];
-        my $sink_object        = $self->[_sink_object_];
-        my $fh_tee             = $self->[_fh_tee_];
-        my $ris_bli_container  = $self->[_ris_bli_container_];
-        my $rK_weld_left       = $self->[_rK_weld_left_];
-
         if ( !defined($K_first) ) {
 
             # Empty line: This can happen if tokens are deleted, for example
             # with the -mangle parameter
             return;
         }
+        $rLL                     = $self->[_rLL_];
+        $radjusted_levels        = $self->[_radjusted_levels_];
+        $rparent_of_seqno        = $self->[_rparent_of_seqno_];
+        $rdepth_of_opening_seqno = $self->[_rdepth_of_opening_seqno_];
+        $rblock_type_of_seqno    = $self->[_rblock_type_of_seqno_];
+
+        #---------------------------------
+        # end initialize closure variables
+        #---------------------------------
 
+        # This flag will become nobreak_to_go and should be set to 2 to prevent
+        # a line break AFTER the current token.
         $no_internal_newlines = 0;
         if ( !$rOpts_add_newlines || $CODE_type eq 'NIN' ) {
             $no_internal_newlines = 2;
         }
 
-        $side_comment_follows = 0;
+        my $input_line = $line_of_tokens->{_line_text};
+
         my $is_comment =
           ( $K_first == $K_last && $rLL->[$K_first]->[_TYPE_] eq '#' );
         my $is_static_block_comment_without_leading_space =
@@ -10591,6 +12585,7 @@ EOM
         # Add interline blank if any
         my $last_old_nonblank_type   = "b";
         my $first_new_nonblank_token = "";
+        my $K_first_true             = $K_first;
         if ( $max_index_to_go >= 0 ) {
             $last_old_nonblank_type   = $types_to_go[$max_index_to_go];
             $first_new_nonblank_token = $rLL->[$K_first]->[_TOKEN_];
@@ -10607,12 +12602,10 @@ EOM
 
         my $in_quote = $line_of_tokens->{_ending_in_quote};
         $ending_in_quote = $in_quote;
-        my $guessed_indentation_level =
-          $line_of_tokens->{_guessed_indentation_level};
 
-        ######################################
-        # Handle a block (full-line) comment..
-        ######################################
+        #------------------------------------
+        # Handle a block (full-line) comment.
+        #------------------------------------
         if ($is_comment) {
 
             if ( $rOpts->{'delete-block-comments'} ) {
@@ -10621,7 +12614,7 @@ EOM
             }
 
             destroy_one_line_block();
-            $self->end_batch();
+            $self->end_batch() if ( $max_index_to_go >= 0 );
 
             # output a blank line before block comments
             if (
@@ -10651,6 +12644,7 @@ EOM
               )
             {
                 $self->flush();    # switching to new output stream
+                my $file_writer_object = $self->[_file_writer_object_];
                 $file_writer_object->write_blank_code_line();
                 $self->[_last_line_leading_type_] = 'b';
             }
@@ -10673,6 +12667,7 @@ EOM
                 $self->flush();
 
                 # Note that last arg in call here is 'undef' for comments
+                my $file_writer_object = $self->[_file_writer_object_];
                 $file_writer_object->write_code_line(
                     $rtok_first->[_TOKEN_] . "\n", undef );
                 $self->[_last_line_leading_type_] = '#';
@@ -10683,16 +12678,20 @@ EOM
         # compare input/output indentation except for continuation lines
         # (because they have an unknown amount of initial blank space)
         # and lines which are quotes (because they may have been outdented)
-        $self->compare_indentation_levels( $K_first, $guessed_indentation_level,
-            $input_line_number )
-          unless ( $is_hanging_side_comment
+        my $guessed_indentation_level =
+          $line_of_tokens->{_guessed_indentation_level};
+        unless ( $is_hanging_side_comment
             || $rtok_first->[_CI_LEVEL_] > 0
-            || $guessed_indentation_level == 0
-            && $rtok_first->[_TYPE_] eq 'Q' );
+            || $guessed_indentation_level == 0 && $rtok_first->[_TYPE_] eq 'Q' )
+        {
+            my $input_line_number = $line_of_tokens->{_line_number};
+            $self->compare_indentation_levels( $K_first,
+                $guessed_indentation_level, $input_line_number );
+        }
 
-        ##########################
+        #------------------------
         # Handle indentation-only
-        ##########################
+        #------------------------
 
         # NOTE: In previous versions we sent all qw lines out immediately here.
         # No longer doing this: also write a line which is entirely a 'qw' list
@@ -10722,9 +12721,9 @@ EOM
             return;
         }
 
-        ############################
+        #---------------------------
         # Handle all other lines ...
-        ############################
+        #---------------------------
 
         # If we just saw the end of an elsif block, write nag message
         # if we do not see another elseif or an else.
@@ -10737,42 +12736,44 @@ EOM
         }
 
         # This is a good place to kill incomplete one-line blocks
-        if (
-            (
-                   ( $semicolons_before_block_self_destruct == 0 )
-                && ( $max_index_to_go >= 0 )
-                && ( $last_old_nonblank_type eq ';' )
-                && ( $first_new_nonblank_token ne '}' )
-            )
-
-            # Patch for RT #98902. Honor request to break at old commas.
-            || (   $rOpts_break_at_old_comma_breakpoints
-                && $max_index_to_go >= 0
-                && $last_old_nonblank_type eq ',' )
-          )
-        {
-            $forced_breakpoint_to_go[$max_index_to_go] = 1
-              if ($rOpts_break_at_old_comma_breakpoints);
-            destroy_one_line_block();
-            $self->end_batch();
-        }
+        if ( $max_index_to_go >= 0 ) {
+            if (
+                (
+                       ( $semicolons_before_block_self_destruct == 0 )
+                    && ( $last_old_nonblank_type eq ';' )
+                    && ( $first_new_nonblank_token ne '}' )
+                )
 
-        # Keep any requested breaks before this line.  Note that we have to
-        # use the original K_first because it may have been reduced above
-        # to add a blank.  The value of the flag is as follows:
-        #   1 => hard break, flush the batch
-        #   2 => soft break, set breakpoint and continue building the batch
-        if ( $self->[_rbreak_before_Kfirst_]->{$K_first_true} ) {
-            destroy_one_line_block();
-            if ( $self->[_rbreak_before_Kfirst_]->{$K_first_true} == 2 ) {
-                $self->set_forced_breakpoint($max_index_to_go);
-            }
-            else {
+                # Patch for RT #98902. Honor request to break at old commas.
+                || (   $rOpts_break_at_old_comma_breakpoints
+                    && $last_old_nonblank_type eq ',' )
+              )
+            {
+                $forced_breakpoint_to_go[$max_index_to_go] = 1
+                  if ($rOpts_break_at_old_comma_breakpoints);
+                destroy_one_line_block();
                 $self->end_batch();
             }
+
+            # Keep any requested breaks before this line.  Note that we have to
+            # use the original K_first because it may have been reduced above
+            # to add a blank.  The value of the flag is as follows:
+            #   1 => hard break, flush the batch
+            #   2 => soft break, set breakpoint and continue building the batch
+            if ( $self->[_rbreak_before_Kfirst_]->{$K_first_true} ) {
+                destroy_one_line_block();
+                if ( $self->[_rbreak_before_Kfirst_]->{$K_first_true} == 2 ) {
+                    $self->set_forced_breakpoint($max_index_to_go);
+                }
+                else {
+                    $self->end_batch() if ( $max_index_to_go >= 0 );
+                }
+            }
         }
 
+        #--------------------------------------
         # loop to process the tokens one-by-one
+        #--------------------------------------
 
         # We do not want a leading blank if the previous batch just got output
         if ( $max_index_to_go < 0 && $rLL->[$K_first]->[_TYPE_] eq 'b' ) {
 
         foreach my $Ktoken_vars ( $K_first .. $K_last ) {
 
-            my $rtoken_vars   = $rLL->[$Ktoken_vars];
-            my $token         = $rtoken_vars->[_TOKEN_];
-            my $type          = $rtoken_vars->[_TYPE_];
-            my $block_type    = $rtoken_vars->[_BLOCK_TYPE_];
-            my $type_sequence = $rtoken_vars->[_TYPE_SEQUENCE_];
+            my $rtoken_vars = $rLL->[$Ktoken_vars];
+            my $type        = $rtoken_vars->[_TYPE_];
 
             # If we are continuing after seeing a right curly brace, flush
             # buffer unless we see what we are looking for, as in
             #   } else ...
             if ( $rbrace_follower && $type ne 'b' ) {
-
+                my $token = $rtoken_vars->[_TOKEN_];
                 unless ( $rbrace_follower->{$token} ) {
-                    $self->end_batch();
+                    $self->end_batch() if ( $max_index_to_go >= 0 );
                 }
                 $rbrace_follower = undef;
             }
 
-            # Get next nonblank on this line
-            my $next_nonblank_token      = '';
-            my $next_nonblank_token_type = 'b';
+            my (
+                $block_type,       $type_sequence,
+                $is_opening_BLOCK, $is_closing_BLOCK,
+                $nobreak_BEFORE_BLOCK
+            );
+            if ( $rtoken_vars->[_TYPE_SEQUENCE_] ) {
+
+                my $token = $rtoken_vars->[_TOKEN_];
+                $type_sequence = $rtoken_vars->[_TYPE_SEQUENCE_];
+                $block_type    = $rblock_type_of_seqno->{$type_sequence};
+
+                if (   $block_type
+                    && $token eq $type
+                    && $block_type ne 't'
+                    && !$self->[_rshort_nested_]->{$type_sequence} )
+                {
+
+                    if ( $type eq '{' ) {
+                        $is_opening_BLOCK     = 1;
+                        $nobreak_BEFORE_BLOCK = $no_internal_newlines;
+                    }
+                    elsif ( $type eq '}' ) {
+                        $is_closing_BLOCK     = 1;
+                        $nobreak_BEFORE_BLOCK = $no_internal_newlines;
+                    }
+                }
+            }
+
+            # Find next nonblank token on this line and look for a side comment
+            my ( $Knnb, $side_comment_follows );
+
+            # if before last token ...
             if ( $Ktoken_vars < $K_last ) {
-                my $Knnb = $Ktoken_vars + 1;
-                if (   $rLL->[$Knnb]->[_TYPE_] eq 'b'
-                    && $Knnb < $K_last )
+                $Knnb = $Ktoken_vars + 1;
+                if (   $Knnb < $K_last
+                    && $rLL->[$Knnb]->[_TYPE_] eq 'b' )
                 {
                     $Knnb++;
                 }
-                $next_nonblank_token      = $rLL->[$Knnb]->[_TOKEN_];
-                $next_nonblank_token_type = $rLL->[$Knnb]->[_TYPE_];
-            }
-
-            # Do not allow breaks which would promote a side comment to a
-            # block comment.  In order to allow a break before an opening
-            # or closing BLOCK, followed by a side comment, those sections
-            # of code will handle this flag separately.
-            $side_comment_follows = ( $next_nonblank_token_type eq '#' );
-            my $is_opening_BLOCK =
-              (      $type eq '{'
-                  && $token eq '{'
-                  && $block_type
-                  && !$rshort_nested->{$type_sequence}
-                  && $block_type ne 't' );
-            my $is_closing_BLOCK =
-              (      $type eq '}'
-                  && $token eq '}'
-                  && $block_type
-                  && !$rshort_nested->{$type_sequence}
-                  && $block_type ne 't' );
-
-            if (   $side_comment_follows
-                && !$is_opening_BLOCK
-                && !$is_closing_BLOCK )
-            {
-                $no_internal_newlines = 1;
+
+                if ( $rLL->[$Knnb]->[_TYPE_] eq '#' ) {
+                    $side_comment_follows = 1;
+
+                    # Do not allow breaks which would promote a side comment to
+                    # a block comment.
+                    $no_internal_newlines = 2;
+                }
+            }
+
+            # if at last token ...
+            else {
+
+                #---------------------
+                # handle side comments
+                #---------------------
+                if ( $type eq '#' ) {
+                    $self->store_token_to_go( $Ktoken_vars, $rtoken_vars );
+                    next;
+                }
+            }
+
+            #--------------
+            # handle blanks
+            #--------------
+            if ( $type eq 'b' ) {
+                $self->store_token_to_go( $Ktoken_vars, $rtoken_vars );
+                next;
+            }
+
+            # Process non-blank and non-comment tokens ...
+
+            #-----------------
+            # handle semicolon
+            #-----------------
+            if ( $type eq ';' ) {
+
+                my $next_nonblank_token_type = 'b';
+                my $next_nonblank_token      = '';
+                if ( defined($Knnb) ) {
+                    $next_nonblank_token      = $rLL->[$Knnb]->[_TOKEN_];
+                    $next_nonblank_token_type = $rLL->[$Knnb]->[_TYPE_];
+                }
+
+                my $break_before_semicolon = ( $Ktoken_vars == $K_first )
+                  && $rOpts_break_at_old_semicolon_breakpoints;
+
+                # kill one-line blocks with too many semicolons
+                $semicolons_before_block_self_destruct--;
+                if (
+                       $break_before_semicolon
+                    || ( $semicolons_before_block_self_destruct < 0 )
+                    || (   $semicolons_before_block_self_destruct == 0
+                        && $next_nonblank_token_type !~ /^[b\}]$/ )
+                  )
+                {
+                    destroy_one_line_block();
+                    $self->end_batch()
+                      if ( $break_before_semicolon
+                        && $max_index_to_go >= 0 );
+                }
+
+                $self->store_token_to_go( $Ktoken_vars, $rtoken_vars );
+
+                $self->end_batch()
+                  unless (
+                    $no_internal_newlines
+                    || (   $rOpts_keep_interior_semicolons
+                        && $Ktoken_vars < $K_last )
+                    || ( $next_nonblank_token eq '}' )
+                  );
+
             }
 
-            # We're only going to handle breaking for code BLOCKS at this
-            # (top) level.  Other indentation breaks will be handled by
-            # sub scan_list, which is better suited to dealing with them.
-            if ($is_opening_BLOCK) {
+            #-----------
+            # handle '{'
+            #-----------
+            elsif ($is_opening_BLOCK) {
 
                 # Tentatively output this token.  This is required before
                 # calling starting_one_line_block.  We may have to unstore
@@ -10858,49 +12930,26 @@ EOM
                 my $keyword_on_same_line = 1;
                 if (
                        $max_index_to_go >= 0
-                    && $last_nonblank_type eq ')'
-                    && ( ( $rtoken_vars->[_SLEVEL_] < $nesting_depth_to_go[0] )
+                    && defined($K_last_nonblank_code)
+                    && $rLL->[$K_last_nonblank_code]->[_TYPE_] eq ')'
+                    && ( ( $rtoken_vars->[_LEVEL_] < $levels_to_go[0] )
                         || $too_long )
                   )
                 {
                     $keyword_on_same_line = 0;
                 }
 
-                # decide if user requested break before '{'
-                my $want_break =
+                # Break before '{' if requested with -bl or -bli flag
+                my $want_break = $self->[_rbrace_left_]->{$type_sequence};
 
-                  # This test was added to minimize changes in -bl formatting
-                  # caused by other changes to fix cases b562 .. b983
-                  # Previously, the -bl flag was being applied almost randomly
-                  # to sort/map/grep/eval blocks, depending on if they were
-                  # flagged as possible one-line blocks.  usually time they
-                  # were not given -bl formatting.  The following flag was
-                  # added to minimize changes to existing formatting.
-                  $is_braces_left_exclude_block{$block_type}
-                  ? 0
-
-                  # use -bl flag if not a sub block of any type
-                  : $block_type !~ /$ANYSUB_PATTERN/
-                  ? $rOpts->{'opening-brace-on-new-line'}
-
-                  # use -sbl flag for a named sub block
-                  : $block_type !~ /$ASUB_PATTERN/
-                  ? $rOpts->{'opening-sub-brace-on-new-line'}
-
-                  # use -asbl flag for an anonymous sub block
-                  : $rOpts->{'opening-anonymous-sub-brace-on-new-line'};
-
-                # Break if requested with -bli flag
-                $want_break ||= $ris_bli_container->{$type_sequence};
-
-                # Do not break if this token is welded to the left
+                # But do not break if this token is welded to the left
                 if ( $total_weld_count
-                    && defined( $rK_weld_left->{$Ktoken_vars} ) )
+                    && defined( $self->[_rK_weld_left_]->{$Ktoken_vars} ) )
                 {
                     $want_break = 0;
                 }
 
-                # Break before an opening '{' ...
+                # Break BEFORE an opening '{' ...
                 if (
 
                     # if requested
@@ -10913,38 +12962,54 @@ EOM
                     # it will be outdented (eval.t, overload.t), and the user
                     # has not insisted on keeping it on the right
                     || (   !$keyword_on_same_line
-                        && !$rOpts->{'opening-brace-always-on-right'} )
+                        && !$rOpts_opening_brace_always_on_right )
                   )
                 {
 
                     # but only if allowed
-                    unless ($no_internal_newlines) {
+                    unless ($nobreak_BEFORE_BLOCK) {
 
                         # since we already stored this token, we must unstore it
                         $self->unstore_token_to_go();
 
                         # then output the line
-                        $self->end_batch();
+                        $self->end_batch() if ( $max_index_to_go >= 0 );
 
                         # and now store this token at the start of a new line
                         $self->store_token_to_go( $Ktoken_vars, $rtoken_vars );
                     }
                 }
 
-                # Now update for side comment
-                if ($side_comment_follows) { $no_internal_newlines = 1 }
-
                 # now output this line
-                unless ($no_internal_newlines) {
-                    $self->end_batch();
-                }
+                $self->end_batch()
+                  if ( $max_index_to_go >= 0 && !$no_internal_newlines );
             }
 
+            #-----------
+            # handle '}'
+            #-----------
             elsif ($is_closing_BLOCK) {
 
+                my $next_nonblank_token_type = 'b';
+                my $next_nonblank_token      = '';
+                if ( defined($Knnb) ) {
+                    $next_nonblank_token      = $rLL->[$Knnb]->[_TOKEN_];
+                    $next_nonblank_token_type = $rLL->[$Knnb]->[_TYPE_];
+                }
+
                 # If there is a pending one-line block ..
                 if ( $index_start_one_line_block != UNDEFINED_INDEX ) {
 
+                    # Fix for b1208: if a side comment follows this closing
+                    # brace then we must include its length in the length test
+                    # ... unless the -issl flag is set (fixes b1307-1309).
+                    # Assume a minimum of 1 blank space to the comment.
+                    my $added_length =
+                      $side_comment_follows
+                      && !$rOpts_ignore_side_comment_lengths
+                      ? 1 + $rLL->[$Knnb]->[_TOKEN_LENGTH_]
+                      : 0;
+
                     # we have to terminate it if..
                     if (
 
@@ -10952,11 +13017,12 @@ EOM
                         # initial estimate). note: must allow 1 space for this
                         # token
                         $self->excess_line_length( $index_start_one_line_block,
-                            $max_index_to_go ) >= 0
+                            $max_index_to_go ) + $added_length >= 0
 
                         # or if it has too many semicolons
                         || (   $semicolons_before_block_self_destruct == 0
-                            && $last_nonblank_type ne ';' )
+                            && defined($K_last_nonblank_code)
+                            && $rLL->[$K_last_nonblank_code]->[_TYPE_] ne ';' )
                       )
                     {
                         destroy_one_line_block();
@@ -10964,16 +13030,10 @@ EOM
                 }
 
                 # put a break before this closing curly brace if appropriate
-                unless ( $no_internal_newlines
-                    || $index_start_one_line_block != UNDEFINED_INDEX )
-                {
-
-                    # write out everything before this closing curly brace
-                    $self->end_batch();
-                }
-
-                # Now update for side comment
-                if ($side_comment_follows) { $no_internal_newlines = 1 }
+                $self->end_batch()
+                  if ( $max_index_to_go >= 0
+                    && !$nobreak_BEFORE_BLOCK
+                    && $index_start_one_line_block == UNDEFINED_INDEX );
 
                 # store the closing curly brace
                 $self->store_token_to_go( $Ktoken_vars, $rtoken_vars );
@@ -10996,8 +13056,30 @@ EOM
                     # we have to actually make it by removing tentative
                     # breaks that were set within it
                     $self->undo_forced_breakpoint_stack(0);
+
+                    # For -lp, extend the nobreak to include a trailing
+                    # terminal ','.  This is because the -lp indentation was
+                    # not known when making one-line blocks, so we may be able
+                    # to move the line back to fit.  Otherwise we may create a
+                    # needlessly stranded comma on the next line.
+                    my $iend_nobreak = $max_index_to_go - 1;
+                    if (   $rOpts_line_up_parentheses
+                        && $next_nonblank_token_type eq ','
+                        && $Knnb eq $K_last )
+                    {
+                        my $p_seqno = $parent_seqno_to_go[$max_index_to_go];
+                        my $is_excluded =
+                          $self->[_ris_excluded_lp_container_]->{$p_seqno};
+                        $iend_nobreak = $max_index_to_go if ( !$is_excluded );
+                    }
+
                     $self->set_nobreaks( $index_start_one_line_block,
-                        $max_index_to_go - 1 );
+                        $iend_nobreak );
+
+                    # save starting block indexes so that sub correct_lp can
+                    # check and adjust -lp indentation (c098)
+                    push @{$ri_starting_one_line_block},
+                      $index_start_one_line_block;
 
                     # then re-initialize for the next one-line block
                     destroy_one_line_block();
@@ -11031,7 +13113,8 @@ EOM
                 # tokens
                 if ( $block_type eq 'do' ) {
                     $rbrace_follower = \%is_do_follower;
-                    if ( $self->tight_paren_follows( $K_to_go[0], $Ktoken_vars )
+                    if (
+                        $self->tight_paren_follows( $K_to_go[0], $Ktoken_vars )
                       )
                     {
                         $rbrace_follower = { ')' => 1 };
@@ -11053,10 +13136,44 @@ EOM
                 }
 
                 # anonymous sub
-                elsif ( $block_type =~ /$ASUB_PATTERN/ ) {
-
+                elsif ( $self->[_ris_asub_block_]->{$type_sequence} ) {
                     if ($is_one_line_block) {
+
                         $rbrace_follower = \%is_anon_sub_1_brace_follower;
+
+                        # Exceptions to help keep -lp intact, see git #74 ...
+                        # Exception 1: followed by '}' on this line
+                        if (   $Ktoken_vars < $K_last
+                            && $next_nonblank_token eq '}' )
+                        {
+                            $rbrace_follower = undef;
+                            $keep_going      = 1;
+                        }
+
+                        # Exception 2: followed by '}' on next line if -lp set.
+                        # The -lp requirement allows the formatting to follow
+                        # old breaks when -lp is not used, minimizing changes.
+                        # Fixes issue c087.
+                        elsif ($Ktoken_vars == $K_last
+                            && $rOpts_line_up_parentheses )
+                        {
+                            my $K_closing_container =
+                              $self->[_K_closing_container_];
+                            my $K_opening_container =
+                              $self->[_K_opening_container_];
+                            my $p_seqno = $parent_seqno_to_go[$max_index_to_go];
+                            my $Kc      = $K_closing_container->{$p_seqno};
+                            my $is_excluded =
+                              $self->[_ris_excluded_lp_container_]->{$p_seqno};
+                            if (   defined($Kc)
+                                && $rLL->[$Kc]->[_TOKEN_] eq '}'
+                                && !$is_excluded
+                                && $Kc - $Ktoken_vars <= 2 )
+                            {
+                                $rbrace_follower = undef;
+                                $keep_going      = 1;
+                            }
+                        }
                     }
                     else {
                         $rbrace_follower = \%is_anon_sub_brace_follower;
@@ -11108,50 +13225,23 @@ EOM
 
                     unless ( $rbrace_follower->{$next_nonblank_token} ) {
                         $self->end_batch()
-                          unless ($no_internal_newlines);
+                          unless ( $no_internal_newlines
+                            || $max_index_to_go < 0 );
                     }
                     $rbrace_follower = undef;
                 }
 
                 else {
                     $self->end_batch()
-                      unless ($no_internal_newlines);
-                }
-
-            }    # end treatment of closing block token
-
-            # handle semicolon
-            elsif ( $type eq ';' ) {
-
-                my $break_before_semicolon = ( $Ktoken_vars == $K_first )
-                  && $rOpts_break_at_old_semicolon_breakpoints;
-
-                # kill one-line blocks with too many semicolons
-                $semicolons_before_block_self_destruct--;
-                if (
-                       $break_before_semicolon
-                    || ( $semicolons_before_block_self_destruct < 0 )
-                    || (   $semicolons_before_block_self_destruct == 0
-                        && $next_nonblank_token_type !~ /^[b\}]$/ )
-                  )
-                {
-                    destroy_one_line_block();
-                    $self->end_batch() if ($break_before_semicolon);
+                      unless ( $no_internal_newlines
+                        || $max_index_to_go < 0 );
                 }
 
-                $self->store_token_to_go( $Ktoken_vars, $rtoken_vars );
-
-                $self->end_batch()
-                  unless (
-                    $no_internal_newlines
-                    || (   $rOpts_keep_interior_semicolons
-                        && $Ktoken_vars < $K_last )
-                    || ( $next_nonblank_token eq '}' )
-                  );
-
-            }
+            } ## end treatment of closing block token
 
+            #------------------------------
             # handle here_doc target string
+            #------------------------------
             elsif ( $type eq 'h' ) {
 
                 # no newlines after seeing here-target
@@ -11160,24 +13250,25 @@ EOM
                 $self->store_token_to_go( $Ktoken_vars, $rtoken_vars );
             }
 
+            #-----------------------------
             # handle all other token types
+            #-----------------------------
             else {
 
                 $self->store_token_to_go( $Ktoken_vars, $rtoken_vars );
-            }
 
-            # remember two previous nonblank OUTPUT tokens
-            if ( $type ne '#' && $type ne 'b' ) {
-                $last_last_nonblank_token  = $last_nonblank_token;
-                $last_last_nonblank_type   = $last_nonblank_type;
-                $last_nonblank_token       = $token;
-                $last_nonblank_type        = $type;
-                $last_nonblank_block_type  = $block_type;
-                $K_last_last_nonblank_code = $K_last_nonblank_code;
-                $K_last_nonblank_code      = $Ktoken_vars;
+                # break after a label if requested
+                if ( $type eq 'J' && $rOpts_break_after_labels == 1 ) {
+                    $self->end_batch()
+                      unless ($no_internal_newlines);
+                }
             }
 
-        }    # end of loop over all tokens in this 'line_of_tokens'
+            # remember two previous nonblank, non-comment OUTPUT tokens
+            $K_last_last_nonblank_code = $K_last_nonblank_code;
+            $K_last_nonblank_code      = $Ktoken_vars;
+
+        } ## end of loop over all tokens in this line
 
         my $type       = $rLL->[$K_last]->[_TYPE_];
         my $break_flag = $self->[_rbreak_after_Klast_]->{$K_last};
@@ -11197,7 +13288,7 @@ EOM
             || $is_VERSION_statement
 
             # to keep a label at the end of a line
-            || $type eq 'J'
+            || ( $type eq 'J' && $rOpts_break_after_labels != 2 )
 
             # if we have a hard break request
             || $break_flag && $break_flag != 2
@@ -11227,7 +13318,7 @@ EOM
           )
         {
             destroy_one_line_block();
-            $self->end_batch();
+            $self->end_batch() if ( $max_index_to_go >= 0 );
         }
 
         # Check for a soft break request
@@ -11314,13 +13405,14 @@ sub tight_paren_follows {
     #   sub xxx ( ... do {  ... } ) {
     #                               ^----- next block_type
     my $K_test = $self->K_next_nonblank($K_oc);
-    if ( defined($K_test) ) {
-        my $block_type = $rLL->[$K_test]->[_BLOCK_TYPE_];
-        if (   $block_type
-            && $rLL->[$K_test]->[_TYPE_] eq '{'
-            && $block_type =~ /$ANYSUB_PATTERN/ )
-        {
-            return 1;
+    if ( defined($K_test) && $rLL->[$K_test]->[_TYPE_] eq '{' ) {
+        my $seqno_test = $rLL->[$K_test]->[_TYPE_SEQUENCE_];
+        if ($seqno_test) {
+            if (   $self->[_ris_asub_block_]->{$seqno_test}
+                || $self->[_ris_sub_block_]->{$seqno_test} )
+            {
+                return 1;
+            }
         }
     }
 
@@ -11390,6 +13482,13 @@ sub tight_paren_follows {
     return 1;
 }
 
+my %is_brace_semicolon_colon;
+
+BEGIN {
+    my @q = qw( { } ; : );
+    @is_brace_semicolon_colon{@q} = (1) x scalar(@q);
+}
+
 sub starting_one_line_block {
 
     # after seeing an opening curly brace, look for the closing brace and see
@@ -11401,10 +13500,11 @@ sub starting_one_line_block {
 
     my ( $self, $Kj, $K_last_nonblank, $K_last ) = @_;
 
-    my $rbreak_container    = $self->[_rbreak_container_];
-    my $rshort_nested       = $self->[_rshort_nested_];
-    my $rLL                 = $self->[_rLL_];
-    my $K_opening_container = $self->[_K_opening_container_];
+    my $rbreak_container     = $self->[_rbreak_container_];
+    my $rshort_nested        = $self->[_rshort_nested_];
+    my $rLL                  = $self->[_rLL_];
+    my $K_opening_container  = $self->[_K_opening_container_];
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
 
     # kill any current block - we can only go 1 deep
     destroy_one_line_block();
@@ -11421,7 +13521,9 @@ sub starting_one_line_block {
     # indicates that a programming change may have caused a flush operation to
     # clean out the previously stored tokens.
     if ( !defined($max_index_to_go) || $max_index_to_go < 0 ) {
-        Fault("program bug: store_token_to_go called incorrectly\n");
+        Fault("program bug: store_token_to_go called incorrectly\n")
+          if (DEVEL_MODE);
+        return 0;
     }
 
     # Return if block should be broken
@@ -11433,7 +13535,8 @@ sub starting_one_line_block {
     my $ris_bli_container = $self->[_ris_bli_container_];
     my $is_bli            = $ris_bli_container->{$type_sequence};
 
-    my $block_type             = $rLL->[$Kj]->[_BLOCK_TYPE_];
+    my $block_type = $rblock_type_of_seqno->{$type_sequence};
+    $block_type = "" unless ( defined($block_type) );
     my $index_max_forced_break = get_index_max_forced_break();
 
     my $previous_nonblank_token = '';
@@ -11446,9 +13549,12 @@ sub starting_one_line_block {
     }
 
     # find the starting keyword for this block (such as 'if', 'else', ...)
-    if (   $max_index_to_go == 0
-        || $block_type =~ /^[\{\}\;\:]$/
-        || $block_type =~ /^package/ )
+    if (
+        $max_index_to_go == 0
+        ##|| $block_type =~ /^[\{\}\;\:]$/
+        || $is_brace_semicolon_colon{$block_type}
+        || substr( $block_type, 0, 7 ) eq 'package'
+      )
     {
         $i_start = $max_index_to_go;
     }
@@ -11457,8 +13563,9 @@ sub starting_one_line_block {
     elsif (
         $i_last_nonblank >= 0
         && (   $previous_nonblank_token eq $block_type
-            || $block_type =~ /$ANYSUB_PATTERN/
-            || $block_type =~ /\(\)/ )
+            || $self->[_ris_asub_block_]->{$type_sequence}
+            || $self->[_ris_sub_block_]->{$type_sequence}
+            || substr( $block_type, -2, 2 ) eq '()' )
       )
     {
         $i_start = $i_last_nonblank;
@@ -11510,8 +13617,9 @@ sub starting_one_line_block {
         #    create( TypeFoo $e) {$bubba}
         # the blocktype would be marked as create()
         my $stripped_block_type = $block_type;
-        $stripped_block_type =~ s/\(\)$//;
-
+        if ( substr( $block_type, -2, 2 ) eq '()' ) {
+            $stripped_block_type = substr( $block_type, 0, -2 );
+        }
         unless ( $tokens_to_go[$i_start] eq $stripped_block_type ) {
             return 0;
         }
@@ -11598,7 +13706,7 @@ sub starting_one_line_block {
         # closing brace.
         elsif ($rLL->[$Ki]->[_TOKEN_] eq '{'
             && $rLL->[$Ki]->[_TYPE_] eq '{'
-            && $rLL->[$Ki]->[_BLOCK_TYPE_]
+            && $rblock_type_of_seqno->{$type_sequence}
             && !$nobreak )
         {
             return 0;
@@ -11607,7 +13715,7 @@ sub starting_one_line_block {
         # if we find our closing brace..
         elsif ($rLL->[$Ki]->[_TOKEN_] eq '}'
             && $rLL->[$Ki]->[_TYPE_] eq '}'
-            && $rLL->[$Ki]->[_BLOCK_TYPE_]
+            && $rblock_type_of_seqno->{$type_sequence}
             && !$nobreak )
         {
 
@@ -11642,31 +13750,48 @@ sub starting_one_line_block {
             # line break logic in sub process_line_of_CODE.
             # When the second line is input it gets recombined by
             # process_line_of_CODE and passed to the output routines.  The
-            # output routines (set_continuation_breaks) do not break it apart
+            # output routines (break_long_lines) do not break it apart
             # because the bond strengths are set to the highest possible value
             # for grep/map/eval/sort blocks, so the first version gets output.
             # It would be possible to fix this by changing bond strengths,
             # but they are high to prevent errors in older versions of perl.
-
+            # See c100 for eval test.
             if (   $Ki < $K_last
-                && $rLL->[$Ki_nonblank]->[_TYPE_] eq '#'
-                && !$is_sort_map_grep{$block_type} )
+                && $rLL->[$K_last]->[_TYPE_] eq '#'
+                && $rLL->[$K_last]->[_LEVEL_] == $rLL->[$Ki]->[_LEVEL_]
+                && !$rOpts_ignore_side_comment_lengths
+                && !$is_sort_map_grep_eval{$block_type}
+                && $K_last - $Ki_nonblank <= 2 )
             {
+                # Only include the side comment for if/else/elsif/unless if it
+                # immediately follows (because the current '$rbrace_follower'
+                # logic for these will give an immediate brake after these
+                # closing braces).  So for example a line like this
+                #     if (...) { ... } ; # very long comment......
+                # will already break like this:
+                #     if (...) { ... }
+                #     ; # very long comment......
+                # so we do not need to include the length of the comment, which
+                # would break the block. Project 'bioperl' has coding like this.
+                if (   $block_type !~ /^(if|else|elsif|unless)$/
+                    || $K_last == $Ki_nonblank )
+                {
+                    $Ki_nonblank = $K_last;
+                    $pos += $rLL->[$Ki_nonblank]->[_TOKEN_LENGTH_];
 
-                $pos += $rLL->[$Ki_nonblank]->[_TOKEN_LENGTH_];
-
-                if ( $Ki_nonblank > $Ki + 1 ) {
+                    if ( $Ki_nonblank > $Ki + 1 ) {
 
-                    # source whitespace could be anything, assume
-                    # at least one space before the hash on output
-                    if ( $rLL->[ $Ki + 1 ]->[_TYPE_] eq 'b' ) {
-                        $pos += 1;
+                        # source whitespace could be anything, assume
+                        # at least one space before the hash on output
+                        if ( $rLL->[ $Ki + 1 ]->[_TYPE_] eq 'b' ) {
+                            $pos += 1;
+                        }
+                        else { $pos += $rLL->[ $Ki + 1 ]->[_TOKEN_LENGTH_] }
                     }
-                    else { $pos += $rLL->[ $Ki + 1 ]->[_TOKEN_LENGTH_] }
-                }
 
-                if ( $pos >= $maximum_line_length ) {
-                    return 0;
+                    if ( $pos >= $maximum_line_length ) {
+                        return 0;
+                    }
                 }
             }
 
@@ -11730,8 +13855,17 @@ sub compare_indentation_levels {
         $structural_indentation_level = $radjusted_levels->[$K_first];
     }
 
-    my $is_closing_block = $rLL->[$K_first]->[_TYPE_] eq '}'
-      && $rLL->[$K_first]->[_BLOCK_TYPE_];
+    # record max structural depth for log file
+    if ( $structural_indentation_level > $self->[_maximum_BLOCK_level_] ) {
+        $self->[_maximum_BLOCK_level_]         = $structural_indentation_level;
+        $self->[_maximum_BLOCK_level_at_line_] = $line_number;
+    }
+
+    my $type_sequence = $rLL->[$K_first]->[_TYPE_SEQUENCE_];
+    my $is_closing_block =
+         $type_sequence
+      && $self->[_rblock_type_of_seqno_]->{$type_sequence}
+      && $rLL->[$K_first]->[_TYPE_] eq '}';
 
     if ( $guessed_indentation_level ne $structural_indentation_level ) {
         $self->[_last_tabbing_disagreement_] = $line_number;
@@ -11744,7 +13878,6 @@ sub compare_indentation_levels {
             if ( !$self->[_first_brace_tabbing_disagreement_] ) {
                 $self->[_first_brace_tabbing_disagreement_] = $line_number;
             }
-
         }
 
         if ( !$self->[_in_tabbing_disagreement_] ) {
@@ -11844,21 +13977,104 @@ sub compare_indentation_levels {
     sub set_forced_breakpoint {
         my ( $self, $i ) = @_;
 
-        return unless defined $i && $i >= 0;
+        # Set a breakpoint AFTER the token at index $i in the _to_go arrays.
 
-        # Back up at a blank in case we need an = break.
-        # This is a backup fix for cases like b932.
-        if ( $i > 0 && $types_to_go[$i] eq 'b' ) { $i-- }
+        # Exceptions:
+        # - If the token at index $i is a blank, backup to $i-1 to
+        #   get to the previous nonblank token.
+        # - For certain tokens, the break may be placed BEFORE the token
+        #   at index $i, depending on user break preference settings.
+        # - If a break is made after an opening token, then a break will
+        #   also be made before the corresponding closing token.
 
-        # no breaks between welded tokens
-        return if ( $total_weld_count && $self->is_welded_right_at_i($i) );
+        # Returns '$i_nonblank':
+        #   = index of the token after which the breakpoint was actually placed
+        #   = undef if breakpoint was not set.
+        my $i_nonblank;
 
-        my $token = $tokens_to_go[$i];
-        my $type  = $types_to_go[$i];
+        if ( !defined($i) || $i < 0 ) {
 
-        # For certain tokens, use user settings to decide if we break before or
-        # after it
-        if ( $break_before_or_after_token{$token}
+            # Calls with bad index $i are harmless but waste time and should
+            # be caught and eliminated during code development.
+            if (DEVEL_MODE) {
+                my ( $a, $b, $c ) = caller();
+                Fault(
+"Bad call to forced breakpoint from $a $b $c ; called with i=$i; please fix\n"
+                );
+            }
+            return;
+        }
+
+        # Break after token $i
+        $i_nonblank = $self->set_forced_breakpoint_AFTER($i);
+
+        # If we break at an opening container..break at the closing
+        my $set_closing;
+        if ( defined($i_nonblank)
+            && $is_opening_sequence_token{ $tokens_to_go[$i_nonblank] } )
+        {
+            $set_closing = 1;
+            $self->set_closing_breakpoint($i_nonblank);
+        }
+
+        DEBUG_FORCE && do {
+            my ( $a, $b, $c ) = caller();
+            my $msg =
+"FORCE $forced_breakpoint_count after call from $a $c with i=$i max=$max_index_to_go";
+            if ( !defined($i_nonblank) ) {
+                $i = "" unless defined($i);
+                $msg .= " but could not set break after i='$i'\n";
+            }
+            else {
+                $msg .= <<EOM;
+set break after $i_nonblank: tok=$tokens_to_go[$i_nonblank] type=$types_to_go[$i_nonblank] nobr=$nobreak_to_go[$i_nonblank]
+EOM
+                if ( defined($set_closing) ) {
+                    $msg .=
+" Also set closing breakpoint corresponding to this token\n";
+                }
+            }
+            print STDOUT $msg;
+        };
+
+        return $i_nonblank;
+    }
+
+    sub set_forced_breakpoint_AFTER {
+        my ( $self, $i ) = @_;
+
+        # This routine is only called by sub set_forced_breakpoint and
+        # sub set_closing_breakpoint.
+
+        # Set a breakpoint AFTER the token at index $i in the _to_go arrays.
+
+        # Exceptions:
+        # - If the token at index $i is a blank, backup to $i-1 to
+        #   get to the previous nonblank token.
+        # - For certain tokens, the break may be placed BEFORE the token
+        #   at index $i, depending on user break preference settings.
+
+        # Returns:
+        #   - the index of the token after which the break was set, or
+        #   - undef if no break was set
+
+        return unless ( defined($i) && $i >= 0 );
+
+        # Back up at a blank so we have a token to examine.
+        # This was added to fix for cases like b932 involving an '=' break.
+        if ( $i > 0 && $types_to_go[$i] eq 'b' ) { $i-- }
+
+        # Never break between welded tokens
+        return
+          if ( $total_weld_count
+            && $self->[_rK_weld_right_]->{ $K_to_go[$i] } );
+
+        my $token = $tokens_to_go[$i];
+        my $type  = $types_to_go[$i];
+
+        # For certain tokens, use user settings to decide if we break before or
+        # after it
+        if ( $break_before_or_after_token{$token}
             && ( $type eq $token || $type eq 'k' ) )
         {
             if ( $want_break_before{$token} && $i >= 0 ) { $i-- }
@@ -11870,21 +14086,6 @@ sub compare_indentation_levels {
         if ( $i >= 0 && $i <= $max_index_to_go ) {
             my $i_nonblank = ( $types_to_go[$i] ne 'b' ) ? $i : $i - 1;
 
-            DEBUG_FORCE && do {
-                my ( $a, $b, $c ) = caller();
-                print STDOUT
-"FORCE $forced_breakpoint_count from $a $c with i=$i_nonblank max=$max_index_to_go tok=$tokens_to_go[$i_nonblank] type=$types_to_go[$i_nonblank] nobr=$nobreak_to_go[$i_nonblank]\n";
-            };
-
-            ######################################################################
-            # NOTE: if we call set_closing_breakpoint below it will then call
-            # this routing back. So there is the possibility of an infinite
-            # loop if a programming error is made. As a precaution, I have
-            # added a check on the forced_breakpoint flag, so that we won't
-            # keep trying to set it.  That will give additional protection
-            # against a loop.
-            ######################################################################
-
             if (   $i_nonblank >= 0
                 && $nobreak_to_go[$i_nonblank] == 0
                 && !$forced_breakpoint_to_go[$i_nonblank] )
@@ -11898,11 +14099,8 @@ sub compare_indentation_levels {
                 $forced_breakpoint_undo_stack[ $forced_breakpoint_undo_count++ ]
                   = $i_nonblank;
 
-                # if we break at an opening container..break at the closing
-                if ( $is_opening_sequence_token{ $tokens_to_go[$i_nonblank] } )
-                {
-                    $self->set_closing_breakpoint($i_nonblank);
-                }
+                # success
+                return $i_nonblank;
             }
         }
         return;
@@ -11932,10 +14130,10 @@ sub compare_indentation_levels {
             my ( $a, $b, $c ) = caller();
 
             # Bad call, can only be due to a recent programming change.
-            # Better stop here.
             Fault(
 "Program Bug: undo_forced_breakpoint_stack from $a $c has bad i=$i_start "
-            );
+            ) if (DEVEL_MODE);
+            return;
         }
 
         while ( $forced_breakpoint_undo_count > $i_start ) {
@@ -11986,17 +14184,14 @@ sub compare_indentation_levels {
 
         if ( $mate_index_to_go[$i_break] >= 0 ) {
 
-            # CAUTION: infinite recursion possible here:
-            #   set_closing_breakpoint calls set_forced_breakpoint, and
-            #   set_forced_breakpoint call set_closing_breakpoint
-            #   ( test files attrib.t, BasicLyx.pm.html).
-            # Don't reduce the '2' in the statement below
+            # Don't reduce the '2' in the statement below.
+            # Test files: attrib.t, BasicLyx.pm.html
             if ( $mate_index_to_go[$i_break] > $i_break + 2 ) {
 
              # break before } ] and ), but sub set_forced_breakpoint will decide
              # to break before or after a ? and :
                 my $inc = ( $tokens_to_go[$i_break] eq '?' ) ? 0 : 1;
-                $self->set_forced_breakpoint(
+                $self->set_forced_breakpoint_AFTER(
                     $mate_index_to_go[$i_break] - $inc );
             }
         }
@@ -12028,10 +14223,15 @@ sub compare_indentation_levels {
     my $peak_batch_size;
     my $batch_count;
 
+    # variables to keep track of unbalanced containers.
+    my %saved_opening_indentation;
+    my @unmatched_opening_indexes_in_this_batch;
+
     sub initialize_grind_batch_of_CODE {
-        @nonblank_lines_at_depth = ();
-        $peak_batch_size         = 0;
-        $batch_count             = 0;
+        @nonblank_lines_at_depth   = ();
+        $peak_batch_size           = 0;
+        $batch_count               = 0;
+        %saved_opening_indentation = ();
         return;
     }
 
@@ -12065,21 +14265,53 @@ sub compare_indentation_levels {
 
     use constant DEBUG_GRIND => 0;
 
+    sub check_grind_input {
+
+        # Check for valid input to sub grind_batch_of_CODE.  An error here
+        # would most likely be due to an error in 'sub store_token_to_go'.
+        my ($self) = @_;
+
+        # Be sure there are tokens in the batch
+        if ( $max_index_to_go < 0 ) {
+            Fault(<<EOM);
+sub grind incorrectly called with max_index_to_go=$max_index_to_go
+EOM
+        }
+        my $Klimit = $self->[_Klimit_];
+
+        # The local batch tokens must be a continous part of the global token
+        # array.
+        my $KK;
+        foreach my $ii ( 0 .. $max_index_to_go ) {
+
+            my $Km = $KK;
+
+            $KK = $K_to_go[$ii];
+            if ( !defined($KK) || $KK < 0 || $KK > $Klimit ) {
+                $KK = '(undef)' unless defined($KK);
+                Fault(<<EOM);
+at batch index at i=$ii, the value of K_to_go[$ii] = '$KK' is out of the valid range (0 - $Klimit)
+EOM
+            }
+
+            if ( $ii > 0 && $KK != $Km + 1 ) {
+                my $im = $ii - 1;
+                Fault(<<EOM);
+Non-sequential K indexes: i=$im has Km=$Km; but i=$ii has K=$KK;  expecting K = Km+1
+EOM
+            }
+        }
+        return;
+    }
+
     sub grind_batch_of_CODE {
 
         my ($self) = @_;
-        my $file_writer_object = $self->[_file_writer_object_];
 
         my $this_batch = $self->[_this_batch_];
         $batch_count++;
 
-        my $starting_in_quote        = $this_batch->[_starting_in_quote_];
-        my $ending_in_quote          = $this_batch->[_ending_in_quote_];
-        my $is_static_block_comment  = $this_batch->[_is_static_block_comment_];
-        my $rK_to_go                 = $this_batch->[_rK_to_go_];
-        my $ris_seqno_controlling_ci = $self->[_ris_seqno_controlling_ci_];
-
-        my $rLL = $self->[_rLL_];
+        $self->check_grind_input() if (DEVEL_MODE);
 
         # This routine is only called from sub flush_batch_of_code, so that
         # routine is a better spot for debugging.
@@ -12089,27 +14321,81 @@ sub compare_indentation_levels {
                 $token = $tokens_to_go[$max_index_to_go];
                 $type  = $types_to_go[$max_index_to_go];
             }
-            my $output_str = join "", @tokens_to_go[ 0 .. $max_index_to_go ];
+            my $output_str = "";
+            if ( $max_index_to_go > 20 ) {
+                my $mm = $max_index_to_go - 10;
+                $output_str = join( "", @tokens_to_go[ 0 .. 10 ] ) . " ... "
+                  . join( "", @tokens_to_go[ $mm .. $max_index_to_go ] );
+            }
+            else {
+                $output_str = join "", @tokens_to_go[ 0 .. $max_index_to_go ];
+            }
             print STDERR <<EOM;
 grind got batch number $batch_count with $max_index_to_go tokens, last type '$type' tok='$token', text:
 $output_str
 EOM
         };
 
-        # Safety check - shouldn't happen. The calling routine must not call
-        # here unless there are tokens in the batch to be processed.  This
-        # fault can only be triggered by a recent programming change.
-        if ( $max_index_to_go < 0 ) {
-            Fault(
-"sub grind incorrectly called with max_index_to_go=$max_index_to_go"
-            );
+        return if ( $max_index_to_go < 0 );
+
+        $self->set_lp_indentation()
+          if ($rOpts_line_up_parentheses);
+
+        #----------------------------
+        # Shortcut for block comments
+        #----------------------------
+        if (
+               $max_index_to_go == 0
+            && $types_to_go[0] eq '#'
+
+            # this shortcut does not work for -lp yet
+            && !$rOpts_line_up_parentheses
+          )
+        {
+            my $ibeg = 0;
+            $this_batch->[_ri_first_]                 = [$ibeg];
+            $this_batch->[_ri_last_]                  = [$ibeg];
+            $this_batch->[_peak_batch_size_]          = $peak_batch_size;
+            $this_batch->[_do_not_pad_]               = 0;
+            $this_batch->[_batch_count_]              = $batch_count;
+            $this_batch->[_rix_seqno_controlling_ci_] = [];
+
+            $self->convey_batch_to_vertical_aligner();
+
+            my $level = $levels_to_go[$ibeg];
+            $self->[_last_last_line_leading_level_] =
+              $self->[_last_line_leading_level_];
+            $self->[_last_line_leading_type_]  = $types_to_go[$ibeg];
+            $self->[_last_line_leading_level_] = $level;
+            $nonblank_lines_at_depth[$level]   = 1;
+            return;
         }
 
-        # Initialize some batch variables
+        #-------------
+        # Normal route
+        #-------------
+
+        my $rLL                      = $self->[_rLL_];
+        my $ris_seqno_controlling_ci = $self->[_ris_seqno_controlling_ci_];
+        my $rwant_container_open     = $self->[_rwant_container_open_];
+
+        my $starting_in_quote       = $this_batch->[_starting_in_quote_];
+        my $ending_in_quote         = $this_batch->[_ending_in_quote_];
+        my $is_static_block_comment = $this_batch->[_is_static_block_comment_];
+
+        #-------------------------------------------------------
+        # Loop over the batch to initialize some batch variables
+        #-------------------------------------------------------
         my $comma_count_in_batch = 0;
         my $ilast_nonblank       = -1;
         my @colon_list;
         my @ix_seqno_controlling_ci;
+        my %comma_arrow_count           = ();
+        my $comma_arrow_count_contained = 0;
+        my @unmatched_closing_indexes_in_this_batch;
+
+        @unmatched_opening_indexes_in_this_batch = ();
+
         for ( my $i = 0 ; $i <= $max_index_to_go ; $i++ ) {
             $bond_strength_to_go[$i] = 0;
             $iprev_to_go[$i]         = $ilast_nonblank;
@@ -12131,11 +14417,10 @@ EOM
                 # This is a good spot to efficiently collect information needed
                 # for breaking lines...
 
-                if ( $type eq ',' ) { $comma_count_in_batch++; }
-
-                # gather info needed by sub set_continuation_breaks
-                my $seqno = $type_sequence_to_go[$i];
-                if ($seqno) {
+                # gather info needed by sub break_long_lines
+                if ( $type_sequence_to_go[$i] ) {
+                    my $seqno = $type_sequence_to_go[$i];
+                    my $token = $tokens_to_go[$i];
 
                     # remember indexes of any tokens controlling xci
                     # in this batch. This list is needed by sub undo_ci.
@@ -12143,23 +14428,69 @@ EOM
                         push @ix_seqno_controlling_ci, $i;
                     }
 
-                    if ( $type eq '?' ) {
-                        push @colon_list, $type;
+                    if ( $is_opening_sequence_token{$token} ) {
+                        if ( $rwant_container_open->{$seqno} ) {
+                            $self->set_forced_breakpoint($i);
+                        }
+                        push @unmatched_opening_indexes_in_this_batch, $i;
+                        if ( $type eq '?' ) {
+                            push @colon_list, $type;
+                        }
                     }
-                    elsif ( $type eq ':' ) {
-                        push @colon_list, $type;
+                    elsif ( $is_closing_sequence_token{$token} ) {
+
+                        if ( $i > 0 && $rwant_container_open->{$seqno} ) {
+                            $self->set_forced_breakpoint( $i - 1 );
+                        }
+
+                        my $i_mate =
+                          pop @unmatched_opening_indexes_in_this_batch;
+                        if ( defined($i_mate) && $i_mate >= 0 ) {
+                            if ( $type_sequence_to_go[$i_mate] ==
+                                $type_sequence_to_go[$i] )
+                            {
+                                $mate_index_to_go[$i]      = $i_mate;
+                                $mate_index_to_go[$i_mate] = $i;
+                                my $seqno = $type_sequence_to_go[$i];
+                                if ( $comma_arrow_count{$seqno} ) {
+                                    $comma_arrow_count_contained +=
+                                      $comma_arrow_count{$seqno};
+                                }
+                            }
+                            else {
+                                push @unmatched_opening_indexes_in_this_batch,
+                                  $i_mate;
+                                push @unmatched_closing_indexes_in_this_batch,
+                                  $i;
+                            }
+                        }
+                        else {
+                            push @unmatched_closing_indexes_in_this_batch, $i;
+                        }
+                        if ( $type eq ':' ) {
+                            push @colon_list, $type;
+                        }
+                    } ## end elsif ( $is_closing_sequence_token...)
+
+                } ## end if ($seqno)
+
+                elsif ( $type eq ',' ) { $comma_count_in_batch++; }
+                elsif ( $tokens_to_go[$i] eq '=>' ) {
+                    if (@unmatched_opening_indexes_in_this_batch) {
+                        my $j = $unmatched_opening_indexes_in_this_batch[-1];
+                        my $seqno = $type_sequence_to_go[$j];
+                        $comma_arrow_count{$seqno}++;
                     }
                 }
-            }
-        }
-
-        my $comma_arrow_count_contained =
-          $self->match_opening_and_closing_tokens();
+            } ## end if ( $type ne 'b' )
+        } ## end for ( my $i = 0 ; $i <=...)
 
-        # tell the -lp option we are outputting a batch so it can close
-        # any unfinished items in its stack
-        finish_lp_batch();
+        my $is_unbalanced_batch = @unmatched_opening_indexes_in_this_batch +
+          @unmatched_closing_indexes_in_this_batch;
 
+        #------------------------
+        # Set special breakpoints
+        #------------------------
         # If this line ends in a code block brace, set breaks at any
         # previous closing code block braces to breakup a chain of code
         # blocks on one line.  This is very rare but can happen for
@@ -12202,6 +14533,10 @@ EOM
             }
         }
 
+        #-----------------------------------------------
+        # insertion of any blank lines before this batch
+        #-----------------------------------------------
+
         my $imin = 0;
         my $imax = $max_index_to_go;
 
         if ( $types_to_go[$imin] eq 'b' ) { $imin++ }
         if ( $types_to_go[$imax] eq 'b' ) { $imax-- }
 
-        # anything left to write?
-        if ( $imin <= $imax ) {
-
-            my $last_line_leading_type  = $self->[_last_line_leading_type_];
-            my $last_line_leading_level = $self->[_last_line_leading_level_];
-            my $last_last_line_leading_level =
-              $self->[_last_last_line_leading_level_];
-
-            # add a blank line before certain key types but not after a comment
-            if ( $last_line_leading_type ne '#' ) {
-                my $want_blank    = 0;
-                my $leading_token = $tokens_to_go[$imin];
-                my $leading_type  = $types_to_go[$imin];
-
-                # blank lines before subs except declarations and one-liners
-                if ( $leading_type eq 'i' ) {
-                    if ( $leading_token =~ /$SUB_PATTERN/ ) {
-                        $want_blank = $rOpts->{'blank-lines-before-subs'}
-                          if ( terminal_type_i( $imin, $imax ) !~ /^[\;\}]$/ );
-                    }
+        if ( $imin > $imax ) {
+            if (DEVEL_MODE) {
+                my $K0  = $K_to_go[0];
+                my $lno = "";
+                if ( defined($K0) ) { $lno = $rLL->[$K0]->[_LINE_INDEX_] + 1 }
+                Fault(<<EOM);
+Strange: received batch containing only blanks near input line $lno: after trimming imin=$imin, imax=$imax
+EOM
+            }
+            return;
+        }
 
-                    # break before all package declarations
-                    elsif ( substr( $leading_token, 0, 8 ) eq 'package ' ) {
-                        $want_blank = $rOpts->{'blank-lines-before-packages'};
-                    }
-                }
+        my $last_line_leading_type  = $self->[_last_line_leading_type_];
+        my $last_line_leading_level = $self->[_last_line_leading_level_];
+        my $last_last_line_leading_level =
+          $self->[_last_last_line_leading_level_];
 
-                # break before certain key blocks except one-liners
-                if ( $leading_type eq 'k' ) {
-                    if ( $leading_token eq 'BEGIN' || $leading_token eq 'END' )
-                    {
-                        $want_blank = $rOpts->{'blank-lines-before-subs'}
-                          if ( terminal_type_i( $imin, $imax ) ne '}' );
-                    }
+        # add a blank line before certain key types but not after a comment
+        if ( $last_line_leading_type ne '#' ) {
+            my $want_blank    = 0;
+            my $leading_token = $tokens_to_go[$imin];
+            my $leading_type  = $types_to_go[$imin];
 
-                    # Break before certain block types if we haven't had a
-                    # break at this level for a while.  This is the
-                    # difficult decision..
-                    elsif ($last_line_leading_type ne 'b'
-                        && $leading_token =~
-                        /^(unless|if|while|until|for|foreach)$/ )
-                    {
-                        my $lc =
-                          $nonblank_lines_at_depth[$last_line_leading_level];
-                        if ( !defined($lc) ) { $lc = 0 }
+            # blank lines before subs except declarations and one-liners
+            if ( $leading_type eq 'i' ) {
+                if (
 
-                       # patch for RT #128216: no blank line inserted at a level
-                       # change
-                        if ( $levels_to_go[$imin] != $last_line_leading_level )
-                        {
-                            $lc = 0;
-                        }
+                    # quick check
+                    (
+                        substr( $leading_token, 0, 3 ) eq 'sub'
+                        || $rOpts_sub_alias_list
+                    )
 
-                        $want_blank =
-                             $rOpts->{'blanks-before-blocks'}
-                          && $lc >= $rOpts->{'long-block-line-count'}
-                          && $self->consecutive_nonblank_lines() >=
-                          $rOpts->{'long-block-line-count'}
-                          && terminal_type_i( $imin, $imax ) ne '}';
-                    }
+                    # slow check
+                    && $leading_token =~ /$SUB_PATTERN/
+                  )
+                {
+                    $want_blank = $rOpts->{'blank-lines-before-subs'}
+                      if ( terminal_type_i( $imin, $imax ) !~ /^[\;\}\,]$/ );
                 }
 
-                # Check for blank lines wanted before a closing brace
-                if ( $leading_token eq '}' ) {
-                    if (   $rOpts->{'blank-lines-before-closing-block'}
-                        && $block_type_to_go[$imin]
-                        && $block_type_to_go[$imin] =~
-                        /$blank_lines_before_closing_block_pattern/ )
-                    {
-                        my $nblanks =
-                          $rOpts->{'blank-lines-before-closing-block'};
-                        if ( $nblanks > $want_blank ) {
-                            $want_blank = $nblanks;
-                        }
-                    }
+                # break before all package declarations
+                elsif ( substr( $leading_token, 0, 8 ) eq 'package ' ) {
+                    $want_blank = $rOpts->{'blank-lines-before-packages'};
                 }
+            }
 
-                if ($want_blank) {
+            # break before certain key blocks except one-liners
+            if ( $leading_type eq 'k' ) {
+                if ( $leading_token eq 'BEGIN' || $leading_token eq 'END' ) {
+                    $want_blank = $rOpts->{'blank-lines-before-subs'}
+                      if ( terminal_type_i( $imin, $imax ) ne '}' );
+                }
+
+                # Break before certain block types if we haven't had a
+                # break at this level for a while.  This is the
+                # difficult decision..
+                elsif ($last_line_leading_type ne 'b'
+                    && $is_if_unless_while_until_for_foreach{$leading_token} )
+                {
+                    my $lc = $nonblank_lines_at_depth[$last_line_leading_level];
+                    if ( !defined($lc) ) { $lc = 0 }
 
-                   # future: send blank line down normal path to VerticalAligner
-                    $self->flush_vertical_aligner();
-                    $file_writer_object->require_blank_code_lines($want_blank);
+                    # patch for RT #128216: no blank line inserted at a level
+                    # change
+                    if ( $levels_to_go[$imin] != $last_line_leading_level ) {
+                        $lc = 0;
+                    }
+
+                    $want_blank =
+                         $rOpts->{'blanks-before-blocks'}
+                      && $lc >= $rOpts->{'long-block-line-count'}
+                      && $self->consecutive_nonblank_lines() >=
+                      $rOpts->{'long-block-line-count'}
+                      && terminal_type_i( $imin, $imax ) ne '}';
                 }
             }
 
-            # update blank line variables and count number of consecutive
-            # non-blank, non-comment lines at this level
-            $last_last_line_leading_level = $last_line_leading_level;
-            $last_line_leading_level      = $levels_to_go[$imin];
-            if ( $last_line_leading_level < 0 ) { $last_line_leading_level = 0 }
-            $last_line_leading_type = $types_to_go[$imin];
-            if (   $last_line_leading_level == $last_last_line_leading_level
-                && $last_line_leading_type ne 'b'
-                && $last_line_leading_type ne '#'
-                && defined( $nonblank_lines_at_depth[$last_line_leading_level] )
-              )
-            {
-                $nonblank_lines_at_depth[$last_line_leading_level]++;
+            # Check for blank lines wanted before a closing brace
+            if ( $leading_token eq '}' ) {
+                if (   $rOpts->{'blank-lines-before-closing-block'}
+                    && $block_type_to_go[$imin]
+                    && $block_type_to_go[$imin] =~
+                    /$blank_lines_before_closing_block_pattern/ )
+                {
+                    my $nblanks = $rOpts->{'blank-lines-before-closing-block'};
+                    if ( $nblanks > $want_blank ) {
+                        $want_blank = $nblanks;
+                    }
+                }
             }
-            else {
-                $nonblank_lines_at_depth[$last_line_leading_level] = 1;
+
+            if ($want_blank) {
+
+                # future: send blank line down normal path to VerticalAligner
+                $self->flush_vertical_aligner();
+                my $file_writer_object = $self->[_file_writer_object_];
+                $file_writer_object->require_blank_code_lines($want_blank);
             }
+        }
 
-            $self->[_last_line_leading_type_]  = $last_line_leading_type;
-            $self->[_last_line_leading_level_] = $last_line_leading_level;
-            $self->[_last_last_line_leading_level_] =
-              $last_last_line_leading_level;
+        # update blank line variables and count number of consecutive
+        # non-blank, non-comment lines at this level
+        $last_last_line_leading_level = $last_line_leading_level;
+        $last_line_leading_level      = $levels_to_go[$imin];
+        if ( $last_line_leading_level < 0 ) { $last_line_leading_level = 0 }
+        $last_line_leading_type = $types_to_go[$imin];
+        if (   $last_line_leading_level == $last_last_line_leading_level
+            && $last_line_leading_type ne 'b'
+            && $last_line_leading_type ne '#'
+            && defined( $nonblank_lines_at_depth[$last_line_leading_level] ) )
+        {
+            $nonblank_lines_at_depth[$last_line_leading_level]++;
+        }
+        else {
+            $nonblank_lines_at_depth[$last_line_leading_level] = 1;
+        }
 
-            # Flag to remember if we called sub 'pad_array_to_go'.
-            # Some routines (scan_list(), set_continuation_breaks() ) need some
-            # extra tokens added at the end of the batch.  Most batches do not
-            # use these routines, so we will avoid calling 'pad_array_to_go'
-            # unless it is needed.
-            my $called_pad_array_to_go;
+        $self->[_last_line_leading_type_]       = $last_line_leading_type;
+        $self->[_last_line_leading_level_]      = $last_line_leading_level;
+        $self->[_last_last_line_leading_level_] = $last_last_line_leading_level;
 
-            # set all forced breakpoints for good list formatting
-            my $is_long_line = $max_index_to_go > 0
-              && $self->excess_line_length( $imin, $max_index_to_go ) > 0;
+        #--------------------------
+        # scan lists and long lines
+        #--------------------------
 
-            my $old_line_count_in_batch =
-              $max_index_to_go == 0
-              ? 1
-              : $self->get_old_line_count( $K_to_go[0],
-                $K_to_go[$max_index_to_go] );
+        # Flag to remember if we called sub 'pad_array_to_go'.
+        # Some routines (break_lists(), break_long_lines() ) need some
+        # extra tokens added at the end of the batch.  Most batches do not
+        # use these routines, so we will avoid calling 'pad_array_to_go'
+        # unless it is needed.
+        my $called_pad_array_to_go;
 
-            if (
-                   $is_long_line
-                || $old_line_count_in_batch > 1
+        # set all forced breakpoints for good list formatting
+        my $is_long_line = $max_index_to_go > 0
+          && $self->excess_line_length( $imin, $max_index_to_go ) > 0;
 
-                # must always call scan_list() with unbalanced batches because
-                # it is maintaining some stacks
-                || is_unbalanced_batch()
+        my $old_line_count_in_batch = 1;
+        if ( $max_index_to_go > 0 ) {
+            my $Kbeg = $K_to_go[0];
+            my $Kend = $K_to_go[$max_index_to_go];
+            $old_line_count_in_batch +=
+              $rLL->[$Kend]->[_LINE_INDEX_] - $rLL->[$Kbeg]->[_LINE_INDEX_];
+        }
 
-                # call scan_list if we might want to break at commas
-                || (
-                    $comma_count_in_batch
-                    && (   $rOpts_maximum_fields_per_table > 0
-                        && $rOpts_maximum_fields_per_table <=
-                        $comma_count_in_batch
-                        || $rOpts_comma_arrow_breakpoints == 0 )
-                )
+        if (
+               $is_long_line
+            || $old_line_count_in_batch > 1
+
+            # must always call break_lists() with unbalanced batches because
+            # it is maintaining some stacks
+            || $is_unbalanced_batch
+
+            # call break_lists if we might want to break at commas
+            || (
+                $comma_count_in_batch
+                && (   $rOpts_maximum_fields_per_table > 0
+                    && $rOpts_maximum_fields_per_table <= $comma_count_in_batch
+                    || $rOpts_comma_arrow_breakpoints == 0 )
+            )
 
-                # call scan_list if user may want to break open some one-line
-                # hash references
-                || (   $comma_arrow_count_contained
-                    && $rOpts_comma_arrow_breakpoints != 3 )
-              )
-            {
-                # add a couple of extra terminal blank tokens
-                $self->pad_array_to_go();
-                $called_pad_array_to_go = 1;
+            # call break_lists if user may want to break open some one-line
+            # hash references
+            || (   $comma_arrow_count_contained
+                && $rOpts_comma_arrow_breakpoints != 3 )
+          )
+        {
+            # add a couple of extra terminal blank tokens
+            $self->pad_array_to_go();
+            $called_pad_array_to_go = 1;
 
-                ## This caused problems in one version of perl for unknown reasons:
-                ## $saw_good_break ||= scan_list();
-                my $sgb = $self->scan_list($is_long_line);
-                $saw_good_break ||= $sgb;
-            }
+            my $sgb = $self->break_lists($is_long_line);
+            $saw_good_break ||= $sgb;
+        }
 
-            # let $ri_first and $ri_last be references to lists of
-            # first and last tokens of line fragments to output..
-            my ( $ri_first, $ri_last );
+        # let $ri_first and $ri_last be references to lists of
+        # first and last tokens of line fragments to output..
+        my ( $ri_first, $ri_last );
 
-            # write a single line if..
-            if (
+        #-------------------------
+        # write a single line if..
+        #-------------------------
+        if (
 
-                # we aren't allowed to add any newlines
-                !$rOpts_add_newlines
+            # we aren't allowed to add any newlines
+            !$rOpts_add_newlines
 
-                # or,
-                || (
+            # or,
+            || (
 
-                    # this line is 'short'
-                    !$is_long_line
+                # this line is 'short'
+                !$is_long_line
 
-                    # and we didn't see a good breakpoint
-                    && !$saw_good_break
+                # and we didn't see a good breakpoint
+                && !$saw_good_break
 
-                    # and we don't already have an interior breakpoint
-                    && !get_forced_breakpoint_count()
-                )
-              )
-            {
-                @{$ri_first} = ($imin);
-                @{$ri_last}  = ($imax);
-            }
+                # and we don't already have an interior breakpoint
+                && !get_forced_breakpoint_count()
+            )
+          )
+        {
+            @{$ri_first} = ($imin);
+            @{$ri_last}  = ($imax);
+        }
 
-            # otherwise use multiple lines
-            else {
+        #-----------------------------
+        # otherwise use multiple lines
+        #-----------------------------
+        else {
 
-                # add a couple of extra terminal blank tokens if we haven't
-                # already done so
-                $self->pad_array_to_go() unless ($called_pad_array_to_go);
+            # add a couple of extra terminal blank tokens if we haven't
+            # already done so
+            $self->pad_array_to_go() unless ($called_pad_array_to_go);
 
-                ( $ri_first, $ri_last ) =
-                  $self->set_continuation_breaks( $saw_good_break,
-                    \@colon_list );
+            ( $ri_first, $ri_last ) =
+              $self->break_long_lines( $saw_good_break, \@colon_list );
 
-                $self->break_all_chain_tokens( $ri_first, $ri_last );
+            $self->break_all_chain_tokens( $ri_first, $ri_last );
 
-                $self->break_equals( $ri_first, $ri_last );
+            $self->break_equals( $ri_first, $ri_last );
 
-                # now we do a correction step to clean this up a bit
-                # (The only time we would not do this is for debugging)
-                if ($rOpts_recombine) {
-                    ( $ri_first, $ri_last ) =
-                      $self->recombine_breakpoints( $ri_first, $ri_last );
-                }
+            # now we do a correction step to clean this up a bit
+            # (The only time we would not do this is for debugging)
+            $self->recombine_breakpoints( $ri_first, $ri_last )
+              if ( $rOpts_recombine && @{$ri_first} > 1 );
 
-                $self->insert_final_ternary_breaks( $ri_first, $ri_last )
-                  if (@colon_list);
-            }
+            $self->insert_final_ternary_breaks( $ri_first, $ri_last )
+              if (@colon_list);
+        }
 
-            $self->insert_breaks_before_list_opening_containers( $ri_first,
-                $ri_last )
-              if ( %break_before_container_types && $max_index_to_go > 0 );
+        $self->insert_breaks_before_list_opening_containers( $ri_first,
+            $ri_last )
+          if ( %break_before_container_types && $max_index_to_go > 0 );
+
+        #-------------------
+        # -lp corrector step
+        #-------------------
+        my $do_not_pad = 0;
+        if ($rOpts_line_up_parentheses) {
+            $do_not_pad = $self->correct_lp_indentation( $ri_first, $ri_last );
+        }
 
-            # do corrector step if -lp option is used
-            my $do_not_pad = 0;
-            if ($rOpts_line_up_parentheses) {
-                $do_not_pad =
-                  $self->correct_lp_indentation( $ri_first, $ri_last );
+        #--------------------------
+        # unmask phantom semicolons
+        #--------------------------
+        if ( !$tokens_to_go[$imax] && $types_to_go[$imax] eq ';' ) {
+            my $i       = $imax;
+            my $tok     = ';';
+            my $tok_len = 1;
+            if ( $want_left_space{';'} != WS_NO ) {
+                $tok     = ' ;';
+                $tok_len = 2;
             }
+            $tokens_to_go[$i]        = $tok;
+            $token_lengths_to_go[$i] = $tok_len;
+            my $KK = $K_to_go[$i];
+            $rLL->[$KK]->[_TOKEN_]        = $tok;
+            $rLL->[$KK]->[_TOKEN_LENGTH_] = $tok_len;
+            my $line_number = 1 + $rLL->[$KK]->[_LINE_INDEX_];
+            $self->note_added_semicolon($line_number);
 
-            # unmask any invisible line-ending semicolon.  They were placed by
-            # sub respace_tokens but we only now know if we actually need them.
-            if ( !$tokens_to_go[$imax] && $types_to_go[$imax] eq ';' ) {
-                my $i       = $imax;
-                my $tok     = ';';
-                my $tok_len = 1;
-                if ( $want_left_space{';'} != WS_NO ) {
-                    $tok     = ' ;';
-                    $tok_len = 2;
-                }
-                $tokens_to_go[$i]        = $tok;
-                $token_lengths_to_go[$i] = $tok_len;
-                my $KK = $K_to_go[$i];
-                $rLL->[$KK]->[_TOKEN_]        = $tok;
-                $rLL->[$KK]->[_TOKEN_LENGTH_] = $tok_len;
-                my $line_number = 1 + $self->get_old_line_index($KK);
-                $self->note_added_semicolon($line_number);
-            }
-
-            if ( $rOpts_one_line_block_semicolons == 0 ) {
-                $self->delete_one_line_semicolons( $ri_first, $ri_last );
-            }
-
-            # The line breaks for this batch of code have been finalized. Now we
-            # can to package the results for further processing.  We will switch
-            # from the local '_to_go' buffer arrays (i-index) back to the global
-            # token arrays (K-index) at this point.
-            my $rlines_K;
-            my $index_error;
-            for ( my $n = 0 ; $n < @{$ri_first} ; $n++ ) {
-                my $ibeg = $ri_first->[$n];
-                my $Kbeg = $K_to_go[$ibeg];
-                my $iend = $ri_last->[$n];
-                my $Kend = $K_to_go[$iend];
-                if ( $iend - $ibeg != $Kend - $Kbeg ) {
-                    $index_error = $n unless defined($index_error);
-                }
-                push @{$rlines_K},
-                  [ $Kbeg, $Kend, $forced_breakpoint_to_go[$iend] ];
-            }
-
-            # Check correctness of the mapping between the i and K token
-            # indexes.  (The K index is the global index, the i index is the
-            # batch index).  It is important to do this check because an error
-            # would be disastrous.  The reason that we should never see an
-            # index error here is that sub 'store_token_to_go' has a check to
-            # make sure that the indexes in batches remain continuous.  Since
-            # sub 'store_token_to_go' controls feeding tokens into batches,
-            # no index discrepancies should occur unless a recent programming
-            # change has introduced a bug.
-            if ( defined($index_error) ) {
-
-                # Temporary debug code - should never get here
-                for ( my $n = 0 ; $n < @{$ri_first} ; $n++ ) {
-                    my $ibeg  = $ri_first->[$n];
-                    my $Kbeg  = $K_to_go[$ibeg];
-                    my $iend  = $ri_last->[$n];
-                    my $Kend  = $K_to_go[$iend];
-                    my $idiff = $iend - $ibeg;
-                    my $Kdiff = $Kend - $Kbeg;
-                    print STDERR <<EOM;
-line $n, irange $ibeg-$iend = $idiff, Krange $Kbeg-$Kend = $Kdiff;
-EOM
-                }
-                Fault(
-                    "Index error at line $index_error; i and K ranges differ");
+            foreach ( $imax .. $max_index_to_go ) {
+                $summed_lengths_to_go[ $_ + 1 ] += $tok_len;
             }
+        }
 
-            $this_batch->[_rlines_K_]        = $rlines_K;
-            $this_batch->[_ibeg0_]           = $ri_first->[0];
-            $this_batch->[_peak_batch_size_] = $peak_batch_size;
-            $this_batch->[_do_not_pad_]      = $do_not_pad;
-            $this_batch->[_batch_count_]     = $batch_count;
-            $this_batch->[_rix_seqno_controlling_ci_] =
-              \@ix_seqno_controlling_ci;
+        if ( $rOpts_one_line_block_semicolons == 0 ) {
+            $self->delete_one_line_semicolons( $ri_first, $ri_last );
+        }
 
-            $self->send_lines_to_vertical_aligner();
+        #--------------------
+        # ship this batch out
+        #--------------------
+        $this_batch->[_ri_first_]                 = $ri_first;
+        $this_batch->[_ri_last_]                  = $ri_last;
+        $this_batch->[_peak_batch_size_]          = $peak_batch_size;
+        $this_batch->[_do_not_pad_]               = $do_not_pad;
+        $this_batch->[_batch_count_]              = $batch_count;
+        $this_batch->[_rix_seqno_controlling_ci_] = \@ix_seqno_controlling_ci;
 
-            # Insert any requested blank lines after an opening brace.  We have
-            # to skip back before any side comment to find the terminal token
-            my $iterm;
-            for ( $iterm = $imax ; $iterm >= $imin ; $iterm-- ) {
-                next if $types_to_go[$iterm] eq '#';
-                next if $types_to_go[$iterm] eq 'b';
-                last;
-            }
+        $self->convey_batch_to_vertical_aligner();
 
-            # write requested number of blank lines after an opening block brace
-            if ( $iterm >= $imin && $types_to_go[$iterm] eq '{' ) {
-                if (   $rOpts->{'blank-lines-after-opening-block'}
-                    && $block_type_to_go[$iterm]
-                    && $block_type_to_go[$iterm] =~
-                    /$blank_lines_after_opening_block_pattern/ )
-                {
-                    my $nblanks = $rOpts->{'blank-lines-after-opening-block'};
-                    $self->flush_vertical_aligner();
-                    $file_writer_object->require_blank_code_lines($nblanks);
+        #-------------------------------------------------------------------
+        # Write requested number of blank lines after an opening block brace
+        #-------------------------------------------------------------------
+        if ($rOpts_blank_lines_after_opening_block) {
+            my $iterm = $imax;
+            if ( $types_to_go[$iterm] eq '#' && $iterm > $imin ) {
+                $iterm -= 1;
+                if ( $types_to_go[$iterm] eq 'b' && $iterm > $imin ) {
+                    $iterm -= 1;
                 }
             }
+
+            if (   $types_to_go[$iterm] eq '{'
+                && $block_type_to_go[$iterm]
+                && $block_type_to_go[$iterm] =~
+                /$blank_lines_after_opening_block_pattern/ )
+            {
+                my $nblanks = $rOpts_blank_lines_after_opening_block;
+                $self->flush_vertical_aligner();
+                my $file_writer_object = $self->[_file_writer_object_];
+                $file_writer_object->require_blank_code_lines($nblanks);
+            }
         }
 
         # Remember the largest batch size processed. This is needed by the
@@ -12541,107 +14858,6 @@ EOM
 
         return;
     }
-} ## end closure grind_batch_of_CODE
-
-{    ## begin closure match_opening_and_closing_tokens
-
-    # closure to keep track of unbalanced containers.
-    # arrays shared by the routines in this block:
-    my %saved_opening_indentation;
-    my @unmatched_opening_indexes_in_this_batch;
-    my @unmatched_closing_indexes_in_this_batch;
-    my %comma_arrow_count;
-
-    sub initialize_saved_opening_indentation {
-        %saved_opening_indentation = ();
-        return;
-    }
-
-    sub is_unbalanced_batch {
-        return @unmatched_opening_indexes_in_this_batch +
-          @unmatched_closing_indexes_in_this_batch;
-    }
-
-    sub match_opening_and_closing_tokens {
-
-        # Match up indexes of opening and closing braces, etc, in this batch.
-        # This has to be done after all tokens are stored because unstoring
-        # of tokens would otherwise cause trouble.
-
-        my ($self)               = @_;
-        my $rwant_container_open = $self->[_rwant_container_open_];
-        my $rparent_of_seqno     = $self->[_rparent_of_seqno_];
-
-        @unmatched_opening_indexes_in_this_batch = ();
-        @unmatched_closing_indexes_in_this_batch = ();
-        %comma_arrow_count                       = ();
-        my $comma_arrow_count_contained = 0;
-        my $parent_seqno = $self->parent_seqno_by_K( $K_to_go[0] );
-
-        foreach my $i ( 0 .. $max_index_to_go ) {
-            $parent_seqno_to_go[$i] = $parent_seqno;
-
-            my $seqno = $type_sequence_to_go[$i];
-            if ($seqno) {
-                my $token = $tokens_to_go[$i];
-                if ( $is_opening_sequence_token{$token} ) {
-                    if ( $is_opening_token{$token} ) {
-                        $parent_seqno = $seqno;
-                    }
-
-                    if ( $rwant_container_open->{$seqno} ) {
-                        $self->set_forced_breakpoint($i);
-                    }
-
-                    push @unmatched_opening_indexes_in_this_batch, $i;
-                }
-                elsif ( $is_closing_sequence_token{$token} ) {
-
-                    if ( $is_closing_token{$token} ) {
-                        $parent_seqno = $rparent_of_seqno->{$seqno};
-                        $parent_seqno = SEQ_ROOT unless defined($parent_seqno);
-                        $parent_seqno_to_go[$i] = $parent_seqno;
-                    }
-
-                    if ( $rwant_container_open->{$seqno} ) {
-                        $self->set_forced_breakpoint( $i - 1 );
-                    }
-
-                    my $i_mate = pop @unmatched_opening_indexes_in_this_batch;
-                    if ( defined($i_mate) && $i_mate >= 0 ) {
-                        if ( $type_sequence_to_go[$i_mate] ==
-                            $type_sequence_to_go[$i] )
-                        {
-                            $mate_index_to_go[$i]      = $i_mate;
-                            $mate_index_to_go[$i_mate] = $i;
-                            my $seqno = $type_sequence_to_go[$i];
-                            if ( $comma_arrow_count{$seqno} ) {
-                                $comma_arrow_count_contained +=
-                                  $comma_arrow_count{$seqno};
-                            }
-                        }
-                        else {
-                            push @unmatched_opening_indexes_in_this_batch,
-                              $i_mate;
-                            push @unmatched_closing_indexes_in_this_batch, $i;
-                        }
-                    }
-                    else {
-                        push @unmatched_closing_indexes_in_this_batch, $i;
-                    }
-                }
-            }
-            elsif ( $tokens_to_go[$i] eq '=>' ) {
-                if (@unmatched_opening_indexes_in_this_batch) {
-                    my $j     = $unmatched_opening_indexes_in_this_batch[-1];
-                    my $seqno = $type_sequence_to_go[$j];
-                    $comma_arrow_count{$seqno}++;
-                }
-            }
-        }
-
-        return $comma_arrow_count_contained;
-    }
 
     sub save_opening_indentation {
 
@@ -12707,7 +14923,7 @@ EOM
 
         return ( $indent, $offset, $is_leading, $exists );
     }
-} ## end closure match_opening_and_closing_tokens
+} ## end closure grind_batch_of_CODE
 
 sub lookup_opening_indentation {
 
@@ -12735,9 +14951,11 @@ sub lookup_opening_indentation {
     if ( !@{$ri_last} ) {
 
         # An error here implies a bug introduced by a recent program change.
-        # Every batch of code has lines.
-        Fault("Error in opening_indentation: no lines");
-        return;
+        # Every batch of code has lines, so this should never happen.
+        if (DEVEL_MODE) {
+            Fault("Error in opening_indentation: no lines");
+        }
+        return ( 0, 0, 0 );
     }
 
     my $nline = $rindentation_list->[0];    # line number of previous lookup
@@ -12755,83 +14973,73 @@ sub lookup_opening_indentation {
     # We better stop here.
     else {
         my $i_last_line = $ri_last->[-1];
-        Fault(<<EOM);
+        if (DEVEL_MODE) {
+            Fault(<<EOM);
 Program bug in call to lookup_opening_indentation - index out of range
  called with index i_opening=$i_opening  > $i_last_line = max index of last line
 This batch has max index = $max_index_to_go,
 EOM
-        report_definite_bug();    # old coding, will not get here
+        }
         $nline = $#{$ri_last};
     }
 
     $rindentation_list->[0] =
-      $nline;                     # save line number to start looking next call
+      $nline;    # save line number to start looking next call
     my $ibeg       = $ri_start->[$nline];
     my $offset     = token_sequence_length( $ibeg, $i_opening ) - 1;
     my $is_leading = ( $ibeg == $i_opening );
     return ( $rindentation_list->[ $nline + 1 ], $offset, $is_leading );
 }
 
-{    ## begin closure terminal_type_i
+sub terminal_type_i {
 
-    my %is_sort_map_grep_eval_do;
+    #  returns type of last token on this line (terminal token), as follows:
+    #  returns # for a full-line comment
+    #  returns ' ' for a blank line
+    #  otherwise returns final token type
 
-    BEGIN {
-        my @q = qw(sort map grep eval do);
-        @is_sort_map_grep_eval_do{@q} = (1) x scalar(@q);
-    }
-
-    sub terminal_type_i {
-
-      #    returns type of last token on this line (terminal token), as follows:
-      #    returns # for a full-line comment
-      #    returns ' ' for a blank line
-      #    otherwise returns final token type
-
-        my ( $ibeg, $iend ) = @_;
-
-        # Start at the end and work backwards
-        my $i      = $iend;
-        my $type_i = $types_to_go[$i];
+    my ( $ibeg, $iend ) = @_;
 
-        # Check for side comment
-        if ( $type_i eq '#' ) {
-            $i--;
-            if ( $i < $ibeg ) {
-                return wantarray ? ( $type_i, $ibeg ) : $type_i;
-            }
-            $type_i = $types_to_go[$i];
-        }
+    # Start at the end and work backwards
+    my $i      = $iend;
+    my $type_i = $types_to_go[$i];
 
-        # Skip past a blank
-        if ( $type_i eq 'b' ) {
-            $i--;
-            if ( $i < $ibeg ) {
-                return wantarray ? ( $type_i, $ibeg ) : $type_i;
-            }
-            $type_i = $types_to_go[$i];
+    # Check for side comment
+    if ( $type_i eq '#' ) {
+        $i--;
+        if ( $i < $ibeg ) {
+            return wantarray ? ( $type_i, $ibeg ) : $type_i;
         }
+        $type_i = $types_to_go[$i];
+    }
 
-        # Found it..make sure it is a BLOCK termination,
-        # but hide a terminal } after sort/grep/map because it is not
-        # necessarily the end of the line.  (terminal.t)
-        my $block_type = $block_type_to_go[$i];
-        if (
-            $type_i eq '}'
-            && ( !$block_type
-                || ( $is_sort_map_grep_eval_do{$block_type} ) )
-          )
-        {
-            $type_i = 'b';
+    # Skip past a blank
+    if ( $type_i eq 'b' ) {
+        $i--;
+        if ( $i < $ibeg ) {
+            return wantarray ? ( $type_i, $ibeg ) : $type_i;
         }
-        return wantarray ? ( $type_i, $i ) : $type_i;
+        $type_i = $types_to_go[$i];
     }
 
-} ## end closure terminal_type_i
+    # Found it..make sure it is a BLOCK termination,
+    # but hide a terminal } after sort/map/grep/eval/do because it is not
+    # necessarily the end of the line.  (terminal.t)
+    my $block_type = $block_type_to_go[$i];
+    if (
+        $type_i eq '}'
+        && (  !$block_type
+            || $is_sort_map_grep_eval_do{$block_type} )
+      )
+    {
+        $type_i = 'b';
+    }
+    return wantarray ? ( $type_i, $i ) : $type_i;
+}
 
 sub pad_array_to_go {
 
-    # To simplify coding in scan_list and set_bond_strengths, it helps to
+    # To simplify coding in break_lists and set_bond_strengths, it helps to
     # create some extra blank tokens at the end of the arrays.  We also add
     # some undef's to help guard against using invalid data.
     my ($self) = @_;
@@ -12850,15 +15058,15 @@ sub pad_array_to_go {
     if ( $is_closing_type{ $types_to_go[$max_index_to_go] } ) {
         if ( $nesting_depth_to_go[$max_index_to_go] <= 0 ) {
 
-            # Nesting depths are equivalent to the _SLEVEL_ variable which is
-            # clipped to be >=0 in sub write_line, so it should not be possible
-            # to get here unless the code has a bracing error which leaves a
-            # closing brace with zero nesting depth.
+            # Nesting depths are set to be >=0 in sub write_line, so it should
+            # not be possible to get here unless the code has a bracing error
+            # which leaves a closing brace with zero nesting depth.
             unless ( get_saw_brace_error() ) {
-                warning(
-"Program bug in pad_array_to_go: hit nesting error which should have been caught\n"
-                );
-                report_definite_bug();
+                if (DEVEL_MODE) {
+                    Fault(<<EOM);
+Program bug in pad_array_to_go: hit nesting error which should have been caught
+EOM
+                }
             }
         }
         else {
@@ -13007,7 +15215,7 @@ sub break_all_chain_tokens {
 sub insert_additional_breaks {
 
     # this routine will add line breaks at requested locations after
-    # sub set_continuation_breaks has made preliminary breaks.
+    # sub break_long_lines has made preliminary breaks.
 
     my ( $self, $ri_break_list, $ri_first, $ri_last ) = @_;
     my $i_f;
@@ -13024,10 +15232,11 @@ sub insert_additional_breaks {
 
             # shouldn't happen unless caller passes bad indexes
             if ( $line_number >= @{$ri_last} ) {
-                warning(
-"Non-fatal program bug: couldn't set break at $i_break_left\n"
-                );
-                report_definite_bug();
+                if (DEVEL_MODE) {
+                    Fault(<<EOM);
+Non-fatal program bug: couldn't set break at $i_break_left
+EOM
+                }
                 return;
             }
             $i_f = $ri_first->[$line_number];
@@ -13050,21 +15259,7 @@ sub insert_additional_breaks {
     return;
 }
 
-sub in_same_container_i {
-
-    # check to see if tokens at i1 and i2 are in the
-    # same container, and not separated by a comma, ? or :
-    # This is an interface between the _to_go arrays to the rLL array
-    my ( $self, $i1, $i2 ) = @_;
-
-    # quick check
-    return if ( $parent_seqno_to_go[$i1] ne $parent_seqno_to_go[$i2] );
-
-    # full check
-    return $self->in_same_container_K( $K_to_go[$i1], $K_to_go[$i2] );
-}
-
-{    ## begin closure in_same_container_K
+{    ## begin closure in_same_container_i
     my $ris_break_token;
     my $ris_comma_token;
 
@@ -13082,21 +15277,30 @@ sub in_same_container_i {
         @{$ris_break_token}{@q} = (1) x scalar(@q);
     }
 
-    sub in_same_container_K {
+    sub in_same_container_i {
 
-        # Check to see if tokens at K1 and K2 are in the same container,
-        # and not separated by certain characters: => , ? : || or
-        # This version uses the newer $rLL data structure.
+        # Check to see if tokens at i1 and i2 are in the same container, and
+        # not separated by certain characters: => , ? : || or
+        # This is an interface between the _to_go arrays to the rLL array
+        my ( $self, $i1, $i2 ) = @_;
 
-        my ( $self, $K1, $K2 ) = @_;
-        if ( $K2 < $K1 ) { ( $K1, $K2 ) = ( $K2, $K1 ) }
-        my $rLL     = $self->[_rLL_];
-        my $depth_1 = $rLL->[$K1]->[_SLEVEL_];
+        # quick check
+        my $parent_seqno_1 = $parent_seqno_to_go[$i1];
+        return if ( $parent_seqno_to_go[$i2] ne $parent_seqno_1 );
+
+        if ( $i2 < $i1 ) { ( $i1, $i2 ) = ( $i2, $i1 ) }
+        my $K1  = $K_to_go[$i1];
+        my $K2  = $K_to_go[$i2];
+        my $rLL = $self->[_rLL_];
+
+        my $depth_1 = $nesting_depth_to_go[$i1];
         return if ( $depth_1 < 0 );
-        return unless ( $rLL->[$K2]->[_SLEVEL_] == $depth_1 );
+
+        # Shouldn't happen since i1 and i2 have same parent:
+        return unless ( $nesting_depth_to_go[$i2] == $depth_1 );
 
         # Select character set to scan for
-        my $type_1 = $rLL->[$K1]->[_TYPE_];
+        my $type_1 = $types_to_go[$i1];
         my $rbreak = ( $type_1 ne ':' ) ? $ris_break_token : $ris_comma_token;
 
         # Fast preliminary loop to verify that tokens are in the same container
@@ -13105,35 +15309,36 @@ sub in_same_container_i {
             $KK = $rLL->[$KK]->[_KNEXT_SEQ_ITEM_];
             last if !defined($KK);
             last if ( $KK >= $K2 );
-            my $depth_K = $rLL->[$KK]->[_SLEVEL_];
-            return if ( $depth_K < $depth_1 );
-            next   if ( $depth_K > $depth_1 );
+            my $ii      = $i1 + $KK - $K1;
+            my $depth_i = $nesting_depth_to_go[$ii];
+            return if ( $depth_i < $depth_1 );
+            next   if ( $depth_i > $depth_1 );
             if ( $type_1 ne ':' ) {
-                my $tok_K = $rLL->[$KK]->[_TOKEN_];
-                return if ( $tok_K eq '?' || $tok_K eq ':' );
+                my $tok_i = $tokens_to_go[$ii];
+                return if ( $tok_i eq '?' || $tok_i eq ':' );
             }
         }
 
         # Slow loop checking for certain characters
 
-        ###########################################################
+        #-----------------------------------------------------
         # This is potentially a slow routine and not critical.
         # For safety just give up for large differences.
         # See test file 'infinite_loop.txt'
-        ###########################################################
-        return if ( $K2 - $K1 > 200 );
+        #-----------------------------------------------------
+        return if ( $i2 - $i1 > 200 );
 
-        foreach my $K ( $K1 + 1 .. $K2 - 1 ) {
+        foreach my $ii ( $i1 + 1 .. $i2 - 1 ) {
 
-            my $depth_K = $rLL->[$K]->[_SLEVEL_];
-            next   if ( $depth_K > $depth_1 );
-            return if ( $depth_K < $depth_1 );    # redundant, checked above
-            my $tok = $rLL->[$K]->[_TOKEN_];
-            return if ( $rbreak->{$tok} );
+            my $depth_i = $nesting_depth_to_go[$ii];
+            next   if ( $depth_i > $depth_1 );
+            return if ( $depth_i < $depth_1 );
+            my $tok_i = $tokens_to_go[$ii];
+            return if ( $rbreak->{$tok_i} );
         }
         return 1;
     }
-} ## end closure in_same_container_K
+} ## end closure in_same_container_i
 
 sub break_equals {
 
@@ -13357,10 +15562,14 @@ sub break_equals {
             next if ($semicolon_count);
 
             # ...ok, then make the semicolon invisible
+            my $len = $token_lengths_to_go[$i_semicolon];
             $tokens_to_go[$i_semicolon]            = "";
             $token_lengths_to_go[$i_semicolon]     = 0;
             $rLL->[$K_semicolon]->[_TOKEN_]        = "";
             $rLL->[$K_semicolon]->[_TOKEN_LENGTH_] = 0;
+            foreach ( $i_semicolon .. $max_index_to_go ) {
+                $summed_lengths_to_go[ $_ + 1 ] -= $len;
+            }
         }
         return;
     }
@@ -13369,32 +15578,46 @@ sub break_equals {
 
     sub recombine_breakpoints {
 
-        # sub set_continuation_breaks is very liberal in setting line breaks
+        # We are given indexes to the current lines:
+        #  $ri_beg = ref to array of BEGinning indexes of each line
+        #  $ri_end = ref to array of ENDing indexes of each line
+        my ( $self, $ri_beg, $ri_end ) = @_;
+
+        # sub break_long_lines is very liberal in setting line breaks
         # for long lines, always setting breaks at good breakpoints, even
         # when that creates small lines.  Sometimes small line fragments
         # are produced which would look better if they were combined.
         # That's the task of this routine.
-        #
-        # We are given indexes to the current lines:
-        # $ri_beg = ref to array of BEGinning indexes of each line
-        # $ri_end = ref to array of ENDing indexes of each line
-        my ( $self, $ri_beg, $ri_end ) = @_;
+
+        # do nothing under extreme stress
+        return if ( $stress_level_alpha < 1 && !DEVEL_MODE );
 
         my $rK_weld_right = $self->[_rK_weld_right_];
         my $rK_weld_left  = $self->[_rK_weld_left_];
 
+        my $nmax = @{$ri_end} - 1;
+        return if ( $nmax <= 0 );
+
+        my $nmax_start = $nmax;
+
         # Make a list of all good joining tokens between the lines
         # n-1 and n.
         my @joint;
-        my $nmax = @{$ri_end} - 1;
-        for my $n ( 1 .. $nmax ) {
-            my $ibeg_1 = $ri_beg->[ $n - 1 ];
-            my $iend_1 = $ri_end->[ $n - 1 ];
-            my $iend_2 = $ri_end->[$n];
-            my $ibeg_2 = $ri_beg->[$n];
 
+        # Break the total batch sub-sections with lengths short enough to
+        # recombine
+        my $rsections = [];
+        my $nbeg      = 0;
+        my $nend;
+        my $nmax_section = 0;
+        foreach my $nn ( 1 .. $nmax ) {
+            my $ibeg_1 = $ri_beg->[ $nn - 1 ];
+            my $iend_1 = $ri_end->[ $nn - 1 ];
+            my $iend_2 = $ri_end->[$nn];
+            my $ibeg_2 = $ri_beg->[$nn];
+
+            # Define the joint variable
             my ( $itok, $itokp, $itokm );
-
             foreach my $itest ( $iend_1, $ibeg_2 ) {
                 my $type = $types_to_go[$itest];
                 if (   $is_math_op{$type}
@@ -13405,314 +15628,408 @@ sub break_equals {
                     $itok = $itest;
                 }
             }
-            $joint[$n] = [$itok];
+            $joint[$nn] = [$itok];
+
+            # Update the section list
+            my $excess = $self->excess_line_length( $ibeg_1, $iend_2, 1 );
+            if (
+                $excess <= 1
+
+                # The number 5 here is an arbitrary small number intended
+                # to keep most small matches in one sub-section.
+                || ( defined($nend) && ( $nn < 5 || $nmax - $nn < 5 ) )
+              )
+            {
+                $nend = $nn;
+            }
+            else {
+                if ( defined($nend) ) {
+                    push @{$rsections}, [ $nbeg, $nend ];
+                    my $num = $nend - $nbeg;
+                    if ( $num > $nmax_section ) { $nmax_section = $num }
+                    $nbeg = $nn;
+                    $nend = undef;
+                }
+                $nbeg = $nn;
+            }
+        }
+        if ( defined($nend) ) {
+            push @{$rsections}, [ $nbeg, $nend ];
+            my $num = $nend - $nbeg;
+            if ( $num > $nmax_section ) { $nmax_section = $num }
         }
 
-        my $more_to_do = 1;
+        my $num_sections = @{$rsections};
 
-        # We keep looping over all of the lines of this batch
-        # until there are no more possible recombinations
-        my $nmax_last = @{$ri_end};
-        my $reverse   = 0;
-        while ($more_to_do) {
-            my $n_best = 0;
-            my $bs_best;
-            my $nmax = @{$ri_end} - 1;
+        # This is potentially an O(n-squared) loop, but not critical, so we can
+        # put a finite limit on the total number of iterations. This is
+        # suggested by issue c118, which pushed about 5.e5 lines through here
+        # and caused an excessive run time.
 
-            # Safety check for infinite loop
-            unless ( $nmax < $nmax_last ) {
-
-                # Shouldn't happen because splice below decreases nmax on each
-                # iteration.  An error can only be due to a recent programming
-                # change.
-                Fault("Program bug-infinite loop in recombine breakpoints\n");
-            }
-            $nmax_last  = $nmax;
-            $more_to_do = 0;
-            my $skip_Section_3;
-            my $leading_amp_count = 0;
-            my $this_line_is_semicolon_terminated;
-
-            # loop over all remaining lines in this batch
-            for my $iter ( 1 .. $nmax ) {
-
-                # alternating sweep direction gives symmetric results
-                # for recombining lines which exceed the line length
-                # such as eval {{{{.... }}}}
-                my $n;
-                if   ($reverse) { $n = 1 + $nmax - $iter; }
-                else            { $n = $iter }
-
-                #----------------------------------------------------------
-                # If we join the current pair of lines,
-                # line $n-1 will become the left part of the joined line
-                # line $n will become the right part of the joined line
-                #
-                # Here are Indexes of the endpoint tokens of the two lines:
-                #
-                #  -----line $n-1--- | -----line $n-----
-                #  $ibeg_1   $iend_1 | $ibeg_2   $iend_2
-                #                    ^
-                #                    |
-                # We want to decide if we should remove the line break
-                # between the tokens at $iend_1 and $ibeg_2
-                #
-                # We will apply a number of ad-hoc tests to see if joining
-                # here will look ok.  The code will just issue a 'next'
-                # command if the join doesn't look good.  If we get through
-                # the gauntlet of tests, the lines will be recombined.
-                #----------------------------------------------------------
-                #
-                # beginning and ending tokens of the lines we are working on
-                my $ibeg_1    = $ri_beg->[ $n - 1 ];
-                my $iend_1    = $ri_end->[ $n - 1 ];
-                my $iend_2    = $ri_end->[$n];
-                my $ibeg_2    = $ri_beg->[$n];
-                my $ibeg_nmax = $ri_beg->[$nmax];
-
-                # combined line cannot be too long
-                my $excess = $self->excess_line_length( $ibeg_1, $iend_2, 1 );
-                next if ( $excess > 0 );
-
-                my $type_iend_1 = $types_to_go[$iend_1];
-                my $type_iend_2 = $types_to_go[$iend_2];
-                my $type_ibeg_1 = $types_to_go[$ibeg_1];
-                my $type_ibeg_2 = $types_to_go[$ibeg_2];
-
-                # terminal token of line 2 if any side comment is ignored:
-                my $iend_2t      = $iend_2;
-                my $type_iend_2t = $type_iend_2;
-
-                # some beginning indexes of other lines, which may not exist
-                my $ibeg_0 = $n > 1          ? $ri_beg->[ $n - 2 ] : -1;
-                my $ibeg_3 = $n < $nmax      ? $ri_beg->[ $n + 1 ] : -1;
-                my $ibeg_4 = $n + 2 <= $nmax ? $ri_beg->[ $n + 2 ] : -1;
-
-                my $bs_tweak = 0;
-
-                #my $depth_increase=( $nesting_depth_to_go[$ibeg_2] -
-                #        $nesting_depth_to_go[$ibeg_1] );
-
-                DEBUG_RECOMBINE && do {
-                    print STDERR
-"RECOMBINE: n=$n imid=$iend_1 if=$ibeg_1 type=$type_ibeg_1 =$tokens_to_go[$ibeg_1] next_type=$type_ibeg_2 next_tok=$tokens_to_go[$ibeg_2]\n";
-                };
+        # Three lines of defence have been put in place to prevent excessive
+        # run times:
+        #  1. do nothing if formatting under stress (c118 was under stress)
+        #  2. break into small sub-sections to decrease the maximum n-squared.
+        #  3. put a finite limit on the number of iterations.
 
-                # If line $n is the last line, we set some flags and
-                # do any special checks for it
-                if ( $n == $nmax ) {
+        # Testing shows that most batches only require one or two iterations.
+        # A very large batch which is broken into sub-sections can require one
+        # iteration per section.  This suggests the limit here, which allows
+        # up to 10 iterations plus one pass per sub-section.
+        my $it_count = 0;
+        my $it_count_max =
+          10 + int( 1000 / ( 1 + $nmax_section ) ) + $num_sections;
 
-                    # a terminal '{' should stay where it is
-                    # unless preceded by a fat comma
-                    next if ( $type_ibeg_2 eq '{' && $type_iend_1 ne '=>' );
+        if ( DEBUG_RECOMBINE > 1 ) {
+            my $max = 0;
+            print STDERR "-----\n$num_sections sections found for nmax=$nmax\n";
+            foreach my $sect ( @{$rsections} ) {
+                my ( $nbeg, $nend ) = @{$sect};
+                my $num = $nend - $nbeg;
+                if ( $num > $max ) { $max = $num }
+                print STDERR "$nbeg $nend\n";
+            }
+            print STDERR "max size=$max of $nmax lines\n";
+        }
 
-                    if (   $type_iend_2 eq '#'
-                        && $iend_2 - $ibeg_2 >= 2
-                        && $types_to_go[ $iend_2 - 1 ] eq 'b' )
-                    {
-                        $iend_2t      = $iend_2 - 2;
-                        $type_iend_2t = $types_to_go[$iend_2t];
-                    }
+        # Loop over all sub-sections.  Note that we have to work backwards
+        # from the end of the batch since the sections use original line
+        # numbers, and the line numbers change as we go.
+        while ( my $section = pop @{$rsections} ) {
+            my ( $nbeg, $nend ) = @{$section};
 
-                    $this_line_is_semicolon_terminated = $type_iend_2t eq ';';
+            # number of ending lines to leave untouched in this pass
+            $nmax = @{$ri_end} - 1;
+            my $num_freeze = $nmax - $nend;
+
+            my $more_to_do = 1;
+
+            # We keep looping over all of the lines of this batch
+            # until there are no more possible recombinations
+            my $nmax_last = $nmax + 1;
+            my $reverse   = 0;
+
+            while ($more_to_do) {
+
+                # Safety check for excess total iterations
+                $it_count++;
+                if ( $it_count > $it_count_max ) {
+                    goto RETURN;
                 }
 
-                #----------------------------------------------------------
-                # Recombine Section 0:
-                # Examine the special token joining this line pair, if any.
-                # Put as many tests in this section to avoid duplicate code and
-                # to make formatting independent of whether breaks are to the
-                # left or right of an operator.
-                #----------------------------------------------------------
+                my $n_best = 0;
+                my $bs_best;
+                my $nmax = @{$ri_end} - 1;
 
-                my ($itok) = @{ $joint[$n] };
-                if ($itok) {
+                # Safety check for infinite loop: the line count must decrease
+                unless ( $nmax < $nmax_last ) {
 
-                    my $type = $types_to_go[$itok];
+                    # Shouldn't happen because splice below decreases nmax on
+                    # each iteration.  An error can only be due to a recent
+                    # programming change.  We better stop here.
+                    if (DEVEL_MODE) {
+                        Fault(
+"Program bug-infinite loop in recombine breakpoints\n"
+                        );
+                    }
+                    $more_to_do = 0;
+                    last;
+                }
+                $nmax_last  = $nmax;
+                $more_to_do = 0;
+                my $skip_Section_3;
+                my $leading_amp_count = 0;
+                my $this_line_is_semicolon_terminated;
+
+                # loop over all remaining lines in this batch
+                my $nstop = $nmax - $num_freeze;
+                for my $iter ( $nbeg + 1 .. $nstop ) {
+
+                    # alternating sweep direction gives symmetric results
+                    # for recombining lines which exceed the line length
+                    # such as eval {{{{.... }}}}
+                    my $n;
+                    if   ($reverse) { $n = $nbeg + 1 + $nstop - $iter; }
+                    else            { $n = $iter }
+
+                    #----------------------------------------------------------
+                    # If we join the current pair of lines,
+                    # line $n-1 will become the left part of the joined line
+                    # line $n will become the right part of the joined line
+                    #
+                    # Here are Indexes of the endpoint tokens of the two lines:
+                    #
+                    #  -----line $n-1--- | -----line $n-----
+                    #  $ibeg_1   $iend_1 | $ibeg_2   $iend_2
+                    #                    ^
+                    #                    |
+                    # We want to decide if we should remove the line break
+                    # between the tokens at $iend_1 and $ibeg_2
+                    #
+                    # We will apply a number of ad-hoc tests to see if joining
+                    # here will look ok.  The code will just issue a 'next'
+                    # command if the join doesn't look good.  If we get through
+                    # the gauntlet of tests, the lines will be recombined.
+                    #----------------------------------------------------------
+                    #
+                    # beginning and ending tokens of the lines we are working on
+                    my $ibeg_1    = $ri_beg->[ $n - 1 ];
+                    my $iend_1    = $ri_end->[ $n - 1 ];
+                    my $iend_2    = $ri_end->[$n];
+                    my $ibeg_2    = $ri_beg->[$n];
+                    my $ibeg_nmax = $ri_beg->[$nmax];
+
+                    # combined line cannot be too long
+                    my $excess =
+                      $self->excess_line_length( $ibeg_1, $iend_2, 1 );
+                    next if ( $excess > 0 );
 
-                    if ( $type eq ':' ) {
+                    my $type_iend_1 = $types_to_go[$iend_1];
+                    my $type_iend_2 = $types_to_go[$iend_2];
+                    my $type_ibeg_1 = $types_to_go[$ibeg_1];
+                    my $type_ibeg_2 = $types_to_go[$ibeg_2];
 
-                        # do not join at a colon unless it disobeys the break
-                        # request
-                        if ( $itok eq $iend_1 ) {
-                            next unless $want_break_before{$type};
-                        }
-                        else {
-                            $leading_amp_count++;
-                            next if $want_break_before{$type};
-                        }
-                    } ## end if ':'
+                    # terminal token of line 2 if any side comment is ignored:
+                    my $iend_2t      = $iend_2;
+                    my $type_iend_2t = $type_iend_2;
+
+                    # some beginning indexes of other lines, which may not exist
+                    my $ibeg_0 = $n > 1          ? $ri_beg->[ $n - 2 ] : -1;
+                    my $ibeg_3 = $n < $nmax      ? $ri_beg->[ $n + 1 ] : -1;
+                    my $ibeg_4 = $n + 2 <= $nmax ? $ri_beg->[ $n + 2 ] : -1;
 
-                    # handle math operators + - * /
-                    elsif ( $is_math_op{$type} ) {
+                    my $bs_tweak = 0;
 
-                        # Combine these lines if this line is a single
-                        # number, or if it is a short term with same
-                        # operator as the previous line.  For example, in
-                        # the following code we will combine all of the
-                        # short terms $A, $B, $C, $D, $E, $F, together
-                        # instead of leaving them one per line:
-                        #  my $time =
-                        #    $A * $B * $C * $D * $E * $F *
-                        #    ( 2. * $eps * $sigma * $area ) *
-                        #    ( 1. / $tcold**3 - 1. / $thot**3 );
+                    #my $depth_increase=( $nesting_depth_to_go[$ibeg_2] -
+                    #        $nesting_depth_to_go[$ibeg_1] );
+
+                    DEBUG_RECOMBINE > 1 && do {
+                        print STDERR
+"RECOMBINE: n=$n imid=$iend_1 if=$ibeg_1 type=$type_ibeg_1 =$tokens_to_go[$ibeg_1] next_type=$type_ibeg_2 next_tok=$tokens_to_go[$ibeg_2]\n";
+                    };
 
-                        # This can be important in math-intensive code.
+                    # If line $n is the last line, we set some flags and
+                    # do any special checks for it
+                    if ( $n == $nmax ) {
 
-                        my $good_combo;
+                        # a terminal '{' should stay where it is
+                        # unless preceded by a fat comma
+                        next if ( $type_ibeg_2 eq '{' && $type_iend_1 ne '=>' );
 
-                        my $itokp  = min( $inext_to_go[$itok],  $iend_2 );
-                        my $itokpp = min( $inext_to_go[$itokp], $iend_2 );
-                        my $itokm  = max( $iprev_to_go[$itok],  $ibeg_1 );
-                        my $itokmm = max( $iprev_to_go[$itokm], $ibeg_1 );
+                        if (   $type_iend_2 eq '#'
+                            && $iend_2 - $ibeg_2 >= 2
+                            && $types_to_go[ $iend_2 - 1 ] eq 'b' )
+                        {
+                            $iend_2t      = $iend_2 - 2;
+                            $type_iend_2t = $types_to_go[$iend_2t];
+                        }
 
-                        # check for a number on the right
-                        if ( $types_to_go[$itokp] eq 'n' ) {
+                        $this_line_is_semicolon_terminated =
+                          $type_iend_2t eq ';';
+                    }
 
-                            # ok if nothing else on right
-                            if ( $itokp == $iend_2 ) {
-                                $good_combo = 1;
+                    #----------------------------------------------------------
+                    # Recombine Section 0:
+                    # Examine the special token joining this line pair, if any.
+                    # Put as many tests in this section to avoid duplicate code
+                    # and to make formatting independent of whether breaks are
+                    # to the left or right of an operator.
+                    #----------------------------------------------------------
+
+                    my ($itok) = @{ $joint[$n] };
+                    if ($itok) {
+
+                        my $type = $types_to_go[$itok];
+
+                        if ( $type eq ':' ) {
+
+                            # do not join at a colon unless it disobeys the
+                            # break request
+                            if ( $itok eq $iend_1 ) {
+                                next unless $want_break_before{$type};
                             }
                             else {
-
-                                # look one more token to right..
-                                # okay if math operator or some termination
-                                $good_combo =
-                                  ( ( $itokpp == $iend_2 )
-                                      && $is_math_op{ $types_to_go[$itokpp] } )
-                                  || $types_to_go[$itokpp] =~ /^[#,;]$/;
+                                $leading_amp_count++;
+                                next if $want_break_before{$type};
                             }
-                        }
+                        } ## end if ':'
 
-                        # check for a number on the left
-                        if ( !$good_combo && $types_to_go[$itokm] eq 'n' ) {
+                        # handle math operators + - * /
+                        elsif ( $is_math_op{$type} ) {
 
-                            # okay if nothing else to left
-                            if ( $itokm == $ibeg_1 ) {
-                                $good_combo = 1;
-                            }
+                            # Combine these lines if this line is a single
+                            # number, or if it is a short term with same
+                            # operator as the previous line.  For example, in
+                            # the following code we will combine all of the
+                            # short terms $A, $B, $C, $D, $E, $F, together
+                            # instead of leaving them one per line:
+                            #  my $time =
+                            #    $A * $B * $C * $D * $E * $F *
+                            #    ( 2. * $eps * $sigma * $area ) *
+                            #    ( 1. / $tcold**3 - 1. / $thot**3 );
 
-                            # otherwise look one more token to left
-                            else {
+                            # This can be important in math-intensive code.
+
+                            my $good_combo;
+
+                            my $itokp  = min( $inext_to_go[$itok],  $iend_2 );
+                            my $itokpp = min( $inext_to_go[$itokp], $iend_2 );
+                            my $itokm  = max( $iprev_to_go[$itok],  $ibeg_1 );
+                            my $itokmm = max( $iprev_to_go[$itokm], $ibeg_1 );
+
+                            # check for a number on the right
+                            if ( $types_to_go[$itokp] eq 'n' ) {
+
+                                # ok if nothing else on right
+                                if ( $itokp == $iend_2 ) {
+                                    $good_combo = 1;
+                                }
+                                else {
 
-                                # okay if math operator, comma, or assignment
-                                $good_combo = ( $itokmm == $ibeg_1 )
-                                  && ( $is_math_op{ $types_to_go[$itokmm] }
-                                    || $types_to_go[$itokmm] =~ /^[,]$/
-                                    || $is_assignment{ $types_to_go[$itokmm] }
-                                  );
+                                    # look one more token to right..
+                                    # okay if math operator or some termination
+                                    $good_combo =
+                                      ( ( $itokpp == $iend_2 )
+                                          && $is_math_op{ $types_to_go[$itokpp]
+                                          } )
+                                      || $types_to_go[$itokpp] =~ /^[#,;]$/;
+                                }
                             }
-                        }
 
-                        # look for a single short token either side of the
-                        # operator
-                        if ( !$good_combo ) {
+                            # check for a number on the left
+                            if ( !$good_combo && $types_to_go[$itokm] eq 'n' ) {
 
-                            # Slight adjustment factor to make results
-                            # independent of break before or after operator in
-                            # long summed lists.  (An operator and a space make
-                            # two spaces).
-                            my $two = ( $itok eq $iend_1 ) ? 2 : 0;
+                                # okay if nothing else to left
+                                if ( $itokm == $ibeg_1 ) {
+                                    $good_combo = 1;
+                                }
 
-                            $good_combo =
+                                # otherwise look one more token to left
+                                else {
 
-                              # numbers or id's on both sides of this joint
-                              $types_to_go[$itokp] =~ /^[in]$/
-                              && $types_to_go[$itokm] =~ /^[in]$/
+                                   # okay if math operator, comma, or assignment
+                                    $good_combo = ( $itokmm == $ibeg_1 )
+                                      && ( $is_math_op{ $types_to_go[$itokmm] }
+                                        || $types_to_go[$itokmm] =~ /^[,]$/
+                                        || $is_assignment{ $types_to_go[$itokmm]
+                                        } );
+                                }
+                            }
 
-                              # one of the two lines must be short:
-                              && (
-                                (
-                                    # no more than 2 nonblank tokens right of
-                                    # joint
-                                    $itokpp == $iend_2
-
-                                    # short
-                                    && token_sequence_length( $itokp, $iend_2 )
-                                    < $two +
-                                    $rOpts_short_concatenation_item_length
-                                )
-                                || (
-                                    # no more than 2 nonblank tokens left of
-                                    # joint
-                                    $itokmm == $ibeg_1
-
-                                    # short
-                                    && token_sequence_length( $ibeg_1, $itokm )
-                                    < 2 - $two +
-                                    $rOpts_short_concatenation_item_length
-                                )
+                            # look for a single short token either side of the
+                            # operator
+                            if ( !$good_combo ) {
 
-                              )
+                                # Slight adjustment factor to make results
+                                # independent of break before or after operator
+                                # in long summed lists.  (An operator and a
+                                # space make two spaces).
+                                my $two = ( $itok eq $iend_1 ) ? 2 : 0;
 
-                              # keep pure terms; don't mix +- with */
-                              && !(
-                                $is_plus_minus{$type}
-                                && (   $is_mult_div{ $types_to_go[$itokmm] }
-                                    || $is_mult_div{ $types_to_go[$itokpp] } )
-                              )
-                              && !(
-                                $is_mult_div{$type}
-                                && (   $is_plus_minus{ $types_to_go[$itokmm] }
-                                    || $is_plus_minus{ $types_to_go[$itokpp] } )
-                              )
+                                $good_combo =
 
-                              ;
-                        }
+                                  # numbers or id's on both sides of this joint
+                                  $types_to_go[$itokp] =~ /^[in]$/
+                                  && $types_to_go[$itokm] =~ /^[in]$/
+
+                                  # one of the two lines must be short:
+                                  && (
+                                    (
+                                        # no more than 2 nonblank tokens right
+                                        # of joint
+                                        $itokpp == $iend_2
+
+                                        # short
+                                        && token_sequence_length(
+                                            $itokp, $iend_2
+                                        ) < $two +
+                                        $rOpts_short_concatenation_item_length
+                                    )
+                                    || (
+                                        # no more than 2 nonblank tokens left of
+                                        # joint
+                                        $itokmm == $ibeg_1
+
+                                        # short
+                                        && token_sequence_length(
+                                            $ibeg_1, $itokm
+                                        ) < 2 - $two +
+                                        $rOpts_short_concatenation_item_length
+                                    )
+
+                                  )
+
+                                  # keep pure terms; don't mix +- with */
+                                  && !(
+                                    $is_plus_minus{$type}
+                                    && (   $is_mult_div{ $types_to_go[$itokmm] }
+                                        || $is_mult_div{ $types_to_go[$itokpp] }
+                                    )
+                                  )
+                                  && !(
+                                    $is_mult_div{$type}
+                                    && ( $is_plus_minus{ $types_to_go[$itokmm] }
+                                        || $is_plus_minus{ $types_to_go[$itokpp]
+                                        } )
+                                  )
+
+                                  ;
+                            }
 
-                        # it is also good to combine if we can reduce to 2 lines
-                        if ( !$good_combo ) {
+                            # it is also good to combine if we can reduce to 2
+                            # lines
+                            if ( !$good_combo ) {
 
-                            # index on other line where same token would be in a
-                            # long chain.
-                            my $iother =
-                              ( $itok == $iend_1 ) ? $iend_2 : $ibeg_1;
+                                # index on other line where same token would be
+                                # in a long chain.
+                                my $iother =
+                                  ( $itok == $iend_1 ) ? $iend_2 : $ibeg_1;
 
-                            $good_combo =
-                                 $n == 2
-                              && $n == $nmax
-                              && $types_to_go[$iother] ne $type;
-                        }
+                                $good_combo =
+                                     $n == 2
+                                  && $n == $nmax
+                                  && $types_to_go[$iother] ne $type;
+                            }
 
-                        next unless ($good_combo);
+                            next unless ($good_combo);
 
-                    } ## end math
+                        } ## end math
 
-                    elsif ( $is_amp_amp{$type} ) {
-                        ##TBD
-                    } ## end &&, ||
+                        elsif ( $is_amp_amp{$type} ) {
+                            ##TBD
+                        } ## end &&, ||
 
-                    elsif ( $is_assignment{$type} ) {
-                        ##TBD
-                    } ## end assignment
-                }
+                        elsif ( $is_assignment{$type} ) {
+                            ##TBD
+                        } ## end assignment
+                    }
 
-                #----------------------------------------------------------
-                # Recombine Section 1:
-                # Join welded nested containers immediately
-                #----------------------------------------------------------
+                    #----------------------------------------------------------
+                    # Recombine Section 1:
+                    # Join welded nested containers immediately
+                    #----------------------------------------------------------
 
-                if (
-                    $total_weld_count
-                    && ( $type_sequence_to_go[$iend_1]
-                        && defined( $rK_weld_right->{ $K_to_go[$iend_1] } )
-                        || $type_sequence_to_go[$ibeg_2]
-                        && defined( $rK_weld_left->{ $K_to_go[$ibeg_2] } ) )
-                  )
-                {
-                    $n_best = $n;
-                    last;
-                }
+                    if (
+                        $total_weld_count
+                        && ( $type_sequence_to_go[$iend_1]
+                            && defined( $rK_weld_right->{ $K_to_go[$iend_1] } )
+                            || $type_sequence_to_go[$ibeg_2]
+                            && defined( $rK_weld_left->{ $K_to_go[$ibeg_2] } ) )
+                      )
+                    {
+                        $n_best = $n;
+                        last;
+                    }
 
-                $reverse = 0;
+                    $reverse = 0;
 
-                #----------------------------------------------------------
-                # Recombine Section 2:
-                # Examine token at $iend_1 (right end of first line of pair)
-                #----------------------------------------------------------
+                    #----------------------------------------------------------
+                    # Recombine Section 2:
+                    # Examine token at $iend_1 (right end of first line of pair)
+                    #----------------------------------------------------------
 
-                # an isolated '}' may join with a ';' terminated segment
-                if ( $type_iend_1 eq '}' ) {
+                    # an isolated '}' may join with a ';' terminated segment
+                    if ( $type_iend_1 eq '}' ) {
 
                     # Check for cases where combining a semicolon terminated
                     # statement with a previous isolated closing paren will
@@ -13745,338 +16062,358 @@ sub break_equals {
                     # trailing clause will be far to the right.
                     #
                     # The logic here is synchronized with the logic in sub
-                    # sub set_adjusted_indentation, which actually does
+                    # sub final_indentation_adjustment, which actually does
                     # the outdenting.
                     #
-                    $skip_Section_3 ||= $this_line_is_semicolon_terminated
-
-                      # only one token on last line
-                      && $ibeg_1 == $iend_1
-
-                      # must be structural paren
-                      && $tokens_to_go[$iend_1] eq ')'
-
-                      # style must allow outdenting,
-                      && !$closing_token_indentation{')'}
-
-                      # only leading '&&', '||', and ':' if no others seen
-                      # (but note: our count made below could be wrong
-                      # due to intervening comments)
-                      && ( $leading_amp_count == 0
-                        || $type_ibeg_2 !~ /^(:|\&\&|\|\|)$/ )
-
-                      # but leading colons probably line up with a
-                      # previous colon or question (count could be wrong).
-                      && $type_ibeg_2 ne ':'
-
-                      # only one step in depth allowed.  this line must not
-                      # begin with a ')' itself.
-                      && ( $nesting_depth_to_go[$iend_1] ==
-                        $nesting_depth_to_go[$iend_2] + 1 );
-
-                    # YVES patch 2 of 2:
-                    # Allow cuddled eval chains, like this:
-                    #   eval {
-                    #       #STUFF;
-                    #       1; # return true
-                    #   } or do {
-                    #       #handle error
-                    #   };
-                    # This patch works together with a patch in
-                    # setting adjusted indentation (where the closing eval
-                    # brace is outdented if possible).
-                    # The problem is that an 'eval' block has continuation
-                    # indentation and it looks better to undo it in some
-                    # cases.  If we do not use this patch we would get:
-                    #   eval {
-                    #       #STUFF;
-                    #       1; # return true
-                    #       }
-                    #       or do {
-                    #       #handle error
-                    #     };
-                    # The alternative, for uncuddled style, is to create
-                    # a patch in set_adjusted_indentation which undoes
-                    # the indentation of a leading line like 'or do {'.
-                    # This doesn't work well with -icb through
-                    if (
-                           $block_type_to_go[$iend_1] eq 'eval'
-                        && !$rOpts->{'line-up-parentheses'}
-                        && !$rOpts->{'indent-closing-brace'}
-                        && $tokens_to_go[$iend_2] eq '{'
-                        && (
-                            ( $type_ibeg_2 =~ /^(\&\&|\|\|)$/ )
-                            || (   $type_ibeg_2 eq 'k'
-                                && $is_and_or{ $tokens_to_go[$ibeg_2] } )
-                            || $is_if_unless{ $tokens_to_go[$ibeg_2] }
-                        )
-                      )
-                    {
-                        $skip_Section_3 ||= 1;
-                    }
+                        $skip_Section_3 ||= $this_line_is_semicolon_terminated
+
+                          # only one token on last line
+                          && $ibeg_1 == $iend_1
+
+                          # must be structural paren
+                          && $tokens_to_go[$iend_1] eq ')'
+
+                          # style must allow outdenting,
+                          && !$closing_token_indentation{')'}
+
+                          # only leading '&&', '||', and ':' if no others seen
+                          # (but note: our count made below could be wrong
+                          # due to intervening comments)
+                          && ( $leading_amp_count == 0
+                            || $type_ibeg_2 !~ /^(:|\&\&|\|\|)$/ )
+
+                          # but leading colons probably line up with a
+                          # previous colon or question (count could be wrong).
+                          && $type_ibeg_2 ne ':'
+
+                          # only one step in depth allowed.  this line must not
+                          # begin with a ')' itself.
+                          && ( $nesting_depth_to_go[$iend_1] ==
+                            $nesting_depth_to_go[$iend_2] + 1 );
+
+                        # YVES patch 2 of 2:
+                        # Allow cuddled eval chains, like this:
+                        #   eval {
+                        #       #STUFF;
+                        #       1; # return true
+                        #   } or do {
+                        #       #handle error
+                        #   };
+                        # This patch works together with a patch in
+                        # setting adjusted indentation (where the closing eval
+                        # brace is outdented if possible).
+                        # The problem is that an 'eval' block has continuation
+                        # indentation and it looks better to undo it in some
+                        # cases.  If we do not use this patch we would get:
+                        #   eval {
+                        #       #STUFF;
+                        #       1; # return true
+                        #       }
+                        #       or do {
+                        #       #handle error
+                        #     };
+                        # The alternative, for uncuddled style, is to create
+                        # a patch in final_indentation_adjustment which undoes
+                        # the indentation of a leading line like 'or do {'.
+                        # This doesn't work well with -icb through
+                        if (
+                               $block_type_to_go[$iend_1] eq 'eval'
+                            && !ref( $leading_spaces_to_go[$iend_1] )
+                            && !$rOpts_indent_closing_brace
+                            && $tokens_to_go[$iend_2] eq '{'
+                            && (
+                                ( $type_ibeg_2 =~ /^(\&\&|\|\|)$/ )
+                                || (   $type_ibeg_2 eq 'k'
+                                    && $is_and_or{ $tokens_to_go[$ibeg_2] } )
+                                || $is_if_unless{ $tokens_to_go[$ibeg_2] }
+                            )
+                          )
+                        {
+                            $skip_Section_3 ||= 1;
+                        }
 
-                    next
-                      unless (
-                        $skip_Section_3
+                        next
+                          unless (
+                            $skip_Section_3
 
-                        # handle '.' and '?' specially below
-                        || ( $type_ibeg_2 =~ /^[\.\?]$/ )
-                      );
-                }
+                            # handle '.' and '?' specially below
+                            || ( $type_ibeg_2 =~ /^[\.\?]$/ )
+                          );
+                    }
 
-                elsif ( $type_iend_1 eq '{' ) {
+                    elsif ( $type_iend_1 eq '{' ) {
 
-                    # YVES
-                    # honor breaks at opening brace
-                    # Added to prevent recombining something like this:
-                    #  } || eval { package main;
-                    next if $forced_breakpoint_to_go[$iend_1];
-                }
+                        # YVES
+                        # honor breaks at opening brace
+                        # Added to prevent recombining something like this:
+                        #  } || eval { package main;
+                        next if $forced_breakpoint_to_go[$iend_1];
+                    }
 
-                # do not recombine lines with ending &&, ||,
-                elsif ( $is_amp_amp{$type_iend_1} ) {
-                    next unless $want_break_before{$type_iend_1};
-                }
+                    # do not recombine lines with ending &&, ||,
+                    elsif ( $is_amp_amp{$type_iend_1} ) {
+                        next unless $want_break_before{$type_iend_1};
+                    }
 
-                # Identify and recombine a broken ?/: chain
-                elsif ( $type_iend_1 eq '?' ) {
+                    # Identify and recombine a broken ?/: chain
+                    elsif ( $type_iend_1 eq '?' ) {
 
-                    # Do not recombine different levels
-                    next
-                      if ( $levels_to_go[$ibeg_1] ne $levels_to_go[$ibeg_2] );
+                        # Do not recombine different levels
+                        next
+                          if (
+                            $levels_to_go[$ibeg_1] ne $levels_to_go[$ibeg_2] );
 
-                    # do not recombine unless next line ends in :
-                    next unless $type_iend_2 eq ':';
-                }
+                        # do not recombine unless next line ends in :
+                        next unless $type_iend_2 eq ':';
+                    }
 
-                # for lines ending in a comma...
-                elsif ( $type_iend_1 eq ',' ) {
+                    # for lines ending in a comma...
+                    elsif ( $type_iend_1 eq ',' ) {
 
-                    # Do not recombine at comma which is following the
-                    # input bias.
-                    # TODO: might be best to make a special flag
-                    next if ( $old_breakpoint_to_go[$iend_1] );
+                        # Do not recombine at comma which is following the
+                        # input bias.
+                        # TODO: might be best to make a special flag
+                        next if ( $old_breakpoint_to_go[$iend_1] );
 
-                    # An isolated '},' may join with an identifier + ';'
-                    # This is useful for the class of a 'bless' statement
-                    # (bless.t)
-                    if (   $type_ibeg_1 eq '}'
-                        && $type_ibeg_2 eq 'i' )
-                    {
-                        next
-                          unless ( ( $ibeg_1 == ( $iend_1 - 1 ) )
-                            && ( $iend_2 == ( $ibeg_2 + 1 ) )
-                            && $this_line_is_semicolon_terminated );
+                        # An isolated '},' may join with an identifier + ';'
+                        # This is useful for the class of a 'bless' statement
+                        # (bless.t)
+                        if (   $type_ibeg_1 eq '}'
+                            && $type_ibeg_2 eq 'i' )
+                        {
+                            next
+                              unless ( ( $ibeg_1 == ( $iend_1 - 1 ) )
+                                && ( $iend_2 == ( $ibeg_2 + 1 ) )
+                                && $this_line_is_semicolon_terminated );
 
-                        # override breakpoint
-                        $forced_breakpoint_to_go[$iend_1] = 0;
-                    }
+                            # override breakpoint
+                            $forced_breakpoint_to_go[$iend_1] = 0;
+                        }
 
-                    # but otherwise ..
-                    else {
+                        # but otherwise ..
+                        else {
 
-                        # do not recombine after a comma unless this will leave
-                        # just 1 more line
-                        next unless ( $n + 1 >= $nmax );
+                            # do not recombine after a comma unless this will
+                            # leave just 1 more line
+                            next unless ( $n + 1 >= $nmax );
 
-                    # do not recombine if there is a change in indentation depth
-                        next
-                          if (
-                            $levels_to_go[$iend_1] != $levels_to_go[$iend_2] );
-
-                        # do not recombine a "complex expression" after a
-                        # comma.  "complex" means no parens.
-                        my $saw_paren;
-                        foreach my $ii ( $ibeg_2 .. $iend_2 ) {
-                            if ( $tokens_to_go[$ii] eq '(' ) {
-                                $saw_paren = 1;
-                                last;
+                            # do not recombine if there is a change in
+                            # indentation depth
+                            next
+                              if ( $levels_to_go[$iend_1] !=
+                                $levels_to_go[$iend_2] );
+
+                            # do not recombine a "complex expression" after a
+                            # comma.  "complex" means no parens.
+                            my $saw_paren;
+                            foreach my $ii ( $ibeg_2 .. $iend_2 ) {
+                                if ( $tokens_to_go[$ii] eq '(' ) {
+                                    $saw_paren = 1;
+                                    last;
+                                }
                             }
+                            next if $saw_paren;
                         }
-                        next if $saw_paren;
                     }
-                }
 
-                # opening paren..
-                elsif ( $type_iend_1 eq '(' ) {
+                    # opening paren..
+                    elsif ( $type_iend_1 eq '(' ) {
 
-                    # No longer doing this
-                }
-
-                elsif ( $type_iend_1 eq ')' ) {
-
-                    # No longer doing this
-                }
-
-                # keep a terminal for-semicolon
-                elsif ( $type_iend_1 eq 'f' ) {
-                    next;
-                }
+                        # No longer doing this
+                    }
 
-                # if '=' at end of line ...
-                elsif ( $is_assignment{$type_iend_1} ) {
+                    elsif ( $type_iend_1 eq ')' ) {
 
-                    # keep break after = if it was in input stream
-                    # this helps prevent 'blinkers'
-                    next if $old_breakpoint_to_go[$iend_1]
+                        # No longer doing this
+                    }
 
-                      # don't strand an isolated '='
-                      && $iend_1 != $ibeg_1;
+                    # keep a terminal for-semicolon
+                    elsif ( $type_iend_1 eq 'f' ) {
+                        next;
+                    }
 
-                    my $is_short_quote =
-                      (      $type_ibeg_2 eq 'Q'
-                          && $ibeg_2 == $iend_2
-                          && token_sequence_length( $ibeg_2, $ibeg_2 ) <
-                          $rOpts_short_concatenation_item_length );
-                    my $is_ternary =
-                      ( $type_ibeg_1 eq '?'
-                          && ( $ibeg_3 >= 0 && $types_to_go[$ibeg_3] eq ':' ) );
+                    # if '=' at end of line ...
+                    elsif ( $is_assignment{$type_iend_1} ) {
 
-                    # always join an isolated '=', a short quote, or if this
-                    # will put ?/: at start of adjacent lines
-                    if (   $ibeg_1 != $iend_1
-                        && !$is_short_quote
-                        && !$is_ternary )
-                    {
+                        # keep break after = if it was in input stream
+                        # this helps prevent 'blinkers'
                         next
-                          unless (
-                            (
+                          if (
+                            $old_breakpoint_to_go[$iend_1]
 
-                                # unless we can reduce this to two lines
-                                $nmax < $n + 2
+                            # don't strand an isolated '='
+                            && $iend_1 != $ibeg_1
+                          );
 
-                             # or three lines, the last with a leading semicolon
-                                || (   $nmax == $n + 2
-                                    && $types_to_go[$ibeg_nmax] eq ';' )
+                        my $is_short_quote =
+                          (      $type_ibeg_2 eq 'Q'
+                              && $ibeg_2 == $iend_2
+                              && token_sequence_length( $ibeg_2, $ibeg_2 ) <
+                              $rOpts_short_concatenation_item_length );
+                        my $is_ternary = (
+                            $type_ibeg_1 eq '?' && ( $ibeg_3 >= 0
+                                && $types_to_go[$ibeg_3] eq ':' )
+                        );
 
-                                # or the next line ends with a here doc
-                                || $type_iend_2 eq 'h'
+                        # always join an isolated '=', a short quote, or if this
+                        # will put ?/: at start of adjacent lines
+                        if (   $ibeg_1 != $iend_1
+                            && !$is_short_quote
+                            && !$is_ternary )
+                        {
+                            next
+                              unless (
+                                (
 
-                               # or the next line ends in an open paren or brace
-                               # and the break hasn't been forced [dima.t]
-                                || (  !$forced_breakpoint_to_go[$iend_1]
-                                    && $type_iend_2 eq '{' )
-                            )
+                                    # unless we can reduce this to two lines
+                                    $nmax < $n + 2
 
-                            # do not recombine if the two lines might align well
-                            # this is a very approximate test for this
-                            && (
+                                    # or three lines, the last with a leading
+                                    # semicolon
+                                    || (   $nmax == $n + 2
+                                        && $types_to_go[$ibeg_nmax] eq ';' )
 
-                              # RT#127633 - the leading tokens are not operators
-                                ( $type_ibeg_2 ne $tokens_to_go[$ibeg_2] )
+                                    # or the next line ends with a here doc
+                                    || $type_iend_2 eq 'h'
 
-                                # or they are different
-                                || (   $ibeg_3 >= 0
-                                    && $type_ibeg_2 ne $types_to_go[$ibeg_3] )
-                            )
-                          );
+                                    # or the next line ends in an open paren or
+                                    # brace and the break hasn't been forced
+                                    # [dima.t]
+                                    || (  !$forced_breakpoint_to_go[$iend_1]
+                                        && $type_iend_2 eq '{' )
+                                )
 
-                        if (
+                                # do not recombine if the two lines might align
+                                # well this is a very approximate test for this
+                                && (
 
-                            # Recombine if we can make two lines
-                            $nmax >= $n + 2
+                                    # RT#127633 - the leading tokens are not
+                                    # operators
+                                    ( $type_ibeg_2 ne $tokens_to_go[$ibeg_2] )
 
-                            # -lp users often prefer this:
-                            #  my $title = function($env, $env, $sysarea,
-                            #                       "bubba Borrower Entry");
-                            #  so we will recombine if -lp is used we have
-                            #  ending comma
-                            && (  !$rOpts_line_up_parentheses
-                                || $type_iend_2 ne ',' )
-                          )
-                        {
-
-                           # otherwise, scan the rhs line up to last token for
-                           # complexity.  Note that we are not counting the last
-                           # token in case it is an opening paren.
-                            my $tv    = 0;
-                            my $depth = $nesting_depth_to_go[$ibeg_2];
-                            foreach my $i ( $ibeg_2 + 1 .. $iend_2 - 1 ) {
-                                if ( $nesting_depth_to_go[$i] != $depth ) {
-                                    $tv++;
-                                    last if ( $tv > 1 );
-                                }
-                                $depth = $nesting_depth_to_go[$i];
-                            }
+                                    # or they are different
+                                    || (   $ibeg_3 >= 0
+                                        && $type_ibeg_2 ne
+                                        $types_to_go[$ibeg_3] )
+                                )
+                              );
 
-                         # ok to recombine if no level changes before last token
-                            if ( $tv > 0 ) {
+                            if (
 
-                                # otherwise, do not recombine if more than two
-                                # level changes.
-                                next if ( $tv > 1 );
+                                # Recombine if we can make two lines
+                                $nmax >= $n + 2
+
+                                # -lp users often prefer this:
+                                #  my $title = function($env, $env, $sysarea,
+                                #                       "bubba Borrower Entry");
+                                #  so we will recombine if -lp is used we have
+                                #  ending comma
+                                && !(
+                                       $ibeg_3 > 0
+                                    && ref( $leading_spaces_to_go[$ibeg_3] )
+                                    && $type_iend_2 eq ','
+                                )
+                              )
+                            {
 
-                              # check total complexity of the two adjacent lines
-                              # that will occur if we do this join
-                                my $istop =
-                                  ( $n < $nmax )
-                                  ? $ri_end->[ $n + 1 ]
-                                  : $iend_2;
-                                foreach my $i ( $iend_2 .. $istop ) {
+                                # otherwise, scan the rhs line up to last token
+                                # for complexity.  Note that we are not
+                                # counting the last token in case it is an
+                                # opening paren.
+                                my $tv    = 0;
+                                my $depth = $nesting_depth_to_go[$ibeg_2];
+                                foreach my $i ( $ibeg_2 + 1 .. $iend_2 - 1 ) {
                                     if ( $nesting_depth_to_go[$i] != $depth ) {
                                         $tv++;
-                                        last if ( $tv > 2 );
+                                        last if ( $tv > 1 );
                                     }
                                     $depth = $nesting_depth_to_go[$i];
                                 }
 
-                        # do not recombine if total is more than 2 level changes
-                                next if ( $tv > 2 );
+                                # ok to recombine if no level changes before
+                                # last token
+                                if ( $tv > 0 ) {
+
+                                    # otherwise, do not recombine if more than
+                                    # two level changes.
+                                    next if ( $tv > 1 );
+
+                                    # check total complexity of the two
+                                    # adjacent lines that will occur if we do
+                                    # this join
+                                    my $istop =
+                                      ( $n < $nmax )
+                                      ? $ri_end->[ $n + 1 ]
+                                      : $iend_2;
+                                    foreach my $i ( $iend_2 .. $istop ) {
+                                        if (
+                                            $nesting_depth_to_go[$i] != $depth )
+                                        {
+                                            $tv++;
+                                            last if ( $tv > 2 );
+                                        }
+                                        $depth = $nesting_depth_to_go[$i];
+                                    }
+
+                                    # do not recombine if total is more than 2
+                                    # level changes
+                                    next if ( $tv > 2 );
+                                }
                             }
                         }
-                    }
 
-                    unless ( $tokens_to_go[$ibeg_2] =~ /^[\{\(\[]$/ ) {
-                        $forced_breakpoint_to_go[$iend_1] = 0;
+                        unless ( $tokens_to_go[$ibeg_2] =~ /^[\{\(\[]$/ ) {
+                            $forced_breakpoint_to_go[$iend_1] = 0;
+                        }
                     }
-                }
 
-                # for keywords..
-                elsif ( $type_iend_1 eq 'k' ) {
+                    # for keywords..
+                    elsif ( $type_iend_1 eq 'k' ) {
 
-                    # make major control keywords stand out
-                    # (recombine.t)
-                    next
-                      if (
+                        # make major control keywords stand out
+                        # (recombine.t)
+                        next
+                          if (
 
-                        #/^(last|next|redo|return)$/
-                        $is_last_next_redo_return{ $tokens_to_go[$iend_1] }
+                            #/^(last|next|redo|return)$/
+                            $is_last_next_redo_return{ $tokens_to_go[$iend_1] }
 
-                        # but only if followed by multiple lines
-                        && $n < $nmax
-                      );
+                            # but only if followed by multiple lines
+                            && $n < $nmax
+                          );
 
-                    if ( $is_and_or{ $tokens_to_go[$iend_1] } ) {
-                        next
-                          unless $want_break_before{ $tokens_to_go[$iend_1] };
+                        if ( $is_and_or{ $tokens_to_go[$iend_1] } ) {
+                            next
+                              unless $want_break_before{ $tokens_to_go[$iend_1]
+                              };
+                        }
                     }
-                }
 
-                #----------------------------------------------------------
-                # Recombine Section 3:
-                # Examine token at $ibeg_2 (left end of second line of pair)
-                #----------------------------------------------------------
+                    #----------------------------------------------------------
+                    # Recombine Section 3:
+                    # Examine token at $ibeg_2 (left end of second line of pair)
+                    #----------------------------------------------------------
 
-                # join lines identified above as capable of
-                # causing an outdented line with leading closing paren
-                # Note that we are skipping the rest of this section
-                # and the rest of the loop to do the join
-                if ($skip_Section_3) {
-                    $forced_breakpoint_to_go[$iend_1] = 0;
-                    $n_best = $n;
-                    last;
-                }
+                    # join lines identified above as capable of
+                    # causing an outdented line with leading closing paren
+                    # Note that we are skipping the rest of this section
+                    # and the rest of the loop to do the join
+                    if ($skip_Section_3) {
+                        $forced_breakpoint_to_go[$iend_1] = 0;
+                        $n_best = $n;
+                        last;
+                    }
 
-                # handle lines with leading &&, ||
-                elsif ( $is_amp_amp{$type_ibeg_2} ) {
+                    # handle lines with leading &&, ||
+                    elsif ( $is_amp_amp{$type_ibeg_2} ) {
 
-                    $leading_amp_count++;
+                        $leading_amp_count++;
 
-                    # ok to recombine if it follows a ? or :
-                    # and is followed by an open paren..
-                    my $ok =
-                      (      $is_ternary{$type_ibeg_1}
-                          && $tokens_to_go[$iend_2] eq '(' )
+                        # ok to recombine if it follows a ? or :
+                        # and is followed by an open paren..
+                        my $ok =
+                          (      $is_ternary{$type_ibeg_1}
+                              && $tokens_to_go[$iend_2] eq '(' )
 
                     # or is followed by a ? or : at same depth
                     #
@@ -14101,59 +16438,71 @@ sub break_equals {
                     # each one as in the second example.  However, it
                     # sometimes makes things worse to check for this because
                     # it prevents multiple recombinations.  So this is not done.
-                      || ( $ibeg_3 >= 0
-                        && $is_ternary{ $types_to_go[$ibeg_3] }
-                        && $nesting_depth_to_go[$ibeg_3] ==
-                        $nesting_depth_to_go[$ibeg_2] );
-
-                    next if !$ok && $want_break_before{$type_ibeg_2};
-                    $forced_breakpoint_to_go[$iend_1] = 0;
-
-                    # tweak the bond strength to give this joint priority
-                    # over ? and :
-                    $bs_tweak = 0.25;
-                }
-
-                # Identify and recombine a broken ?/: chain
-                elsif ( $type_ibeg_2 eq '?' ) {
-
-                    # Do not recombine different levels
-                    my $lev = $levels_to_go[$ibeg_2];
-                    next if ( $lev ne $levels_to_go[$ibeg_1] );
-
-                    # Do not recombine a '?' if either next line or
-                    # previous line does not start with a ':'.  The reasons
-                    # are that (1) no alignment of the ? will be possible
-                    # and (2) the expression is somewhat complex, so the
-                    # '?' is harder to see in the interior of the line.
-                    my $follows_colon = $ibeg_1 >= 0 && $type_ibeg_1 eq ':';
-                    my $precedes_colon =
-                      $ibeg_3 >= 0 && $types_to_go[$ibeg_3] eq ':';
-                    next unless ( $follows_colon || $precedes_colon );
-
-                    # we will always combining a ? line following a : line
-                    if ( !$follows_colon ) {
-
-                        # ...otherwise recombine only if it looks like a chain.
-                        # we will just look at a few nearby lines to see if
-                        # this looks like a chain.
-                        my $local_count = 0;
-                        foreach my $ii ( $ibeg_0, $ibeg_1, $ibeg_3, $ibeg_4 ) {
-                            $local_count++
-                              if $ii >= 0
-                              && $types_to_go[$ii] eq ':'
-                              && $levels_to_go[$ii] == $lev;
+                          || ( $ibeg_3 >= 0
+                            && $is_ternary{ $types_to_go[$ibeg_3] }
+                            && $nesting_depth_to_go[$ibeg_3] ==
+                            $nesting_depth_to_go[$ibeg_2] );
+
+                        # Combine a trailing && term with an || term: fix for
+                        # c060 This is rare but can happen.
+                        $ok ||= 1
+                          if ( $ibeg_3 < 0
+                            && $type_ibeg_2 eq '&&'
+                            && $type_ibeg_1 eq '||'
+                            && $nesting_depth_to_go[$ibeg_2] ==
+                            $nesting_depth_to_go[$ibeg_1] );
+
+                        next if !$ok && $want_break_before{$type_ibeg_2};
+                        $forced_breakpoint_to_go[$iend_1] = 0;
+
+                        # tweak the bond strength to give this joint priority
+                        # over ? and :
+                        $bs_tweak = 0.25;
+                    }
+
+                    # Identify and recombine a broken ?/: chain
+                    elsif ( $type_ibeg_2 eq '?' ) {
+
+                        # Do not recombine different levels
+                        my $lev = $levels_to_go[$ibeg_2];
+                        next if ( $lev ne $levels_to_go[$ibeg_1] );
+
+                        # Do not recombine a '?' if either next line or
+                        # previous line does not start with a ':'.  The reasons
+                        # are that (1) no alignment of the ? will be possible
+                        # and (2) the expression is somewhat complex, so the
+                        # '?' is harder to see in the interior of the line.
+                        my $follows_colon = $ibeg_1 >= 0 && $type_ibeg_1 eq ':';
+                        my $precedes_colon =
+                          $ibeg_3 >= 0 && $types_to_go[$ibeg_3] eq ':';
+                        next unless ( $follows_colon || $precedes_colon );
+
+                        # we will always combining a ? line following a : line
+                        if ( !$follows_colon ) {
+
+                            # ...otherwise recombine only if it looks like a
+                            # chain.  we will just look at a few nearby lines
+                            # to see if this looks like a chain.
+                            my $local_count = 0;
+                            foreach
+                              my $ii ( $ibeg_0, $ibeg_1, $ibeg_3, $ibeg_4 )
+                            {
+                                $local_count++
+                                  if $ii >= 0
+                                  && $types_to_go[$ii] eq ':'
+                                  && $levels_to_go[$ii] == $lev;
+                            }
+                            next unless ( $local_count > 1 );
                         }
-                        next unless ( $local_count > 1 );
+                        $forced_breakpoint_to_go[$iend_1] = 0;
                     }
-                    $forced_breakpoint_to_go[$iend_1] = 0;
-                }
 
-                # do not recombine lines with leading '.'
-                elsif ( $type_ibeg_2 eq '.' ) {
-                    my $i_next_nonblank = min( $inext_to_go[$ibeg_2], $iend_2 );
-                    next
-                      unless (
+                    # do not recombine lines with leading '.'
+                    elsif ( $type_ibeg_2 eq '.' ) {
+                        my $i_next_nonblank =
+                          min( $inext_to_go[$ibeg_2], $iend_2 );
+                        next
+                          unless (
 
                    # ... unless there is just one and we can reduce
                    # this to two lines if we do.  For example, this
@@ -14166,234 +16515,248 @@ sub break_equals {
                    #  $bodyA .= '($dummy, $pat) = &get_next_tex_cmd;'
                    #    . '$args .= $pat;'
 
-                        (
-                               $n == 2
-                            && $n == $nmax
-                            && $type_ibeg_1 ne $type_ibeg_2
-                        )
+                            (
+                                   $n == 2
+                                && $n == $nmax
+                                && $type_ibeg_1 ne $type_ibeg_2
+                            )
 
-                         ... or this would strand a short quote , like this
-                        #                . "some long quote"
-                        #                . "\n";
+                            # ... or this would strand a short quote , like this
+                            #                . "some long quote"
+                            #                . "\n";
 
-                        || (   $types_to_go[$i_next_nonblank] eq 'Q'
-                            && $i_next_nonblank >= $iend_2 - 1
-                            && $token_lengths_to_go[$i_next_nonblank] <
-                            $rOpts_short_concatenation_item_length )
-                      );
-                }
+                            || (   $types_to_go[$i_next_nonblank] eq 'Q'
+                                && $i_next_nonblank >= $iend_2 - 1
+                                && $token_lengths_to_go[$i_next_nonblank] <
+                                $rOpts_short_concatenation_item_length )
+                          );
+                    }
 
-                # handle leading keyword..
-                elsif ( $type_ibeg_2 eq 'k' ) {
+                    # handle leading keyword..
+                    elsif ( $type_ibeg_2 eq 'k' ) {
 
-                    # handle leading "or"
-                    if ( $tokens_to_go[$ibeg_2] eq 'or' ) {
-                        next
-                          unless (
-                            $this_line_is_semicolon_terminated
-                            && (
-                                $type_ibeg_1 eq '}'
-                                || (
+                        # handle leading "or"
+                        if ( $tokens_to_go[$ibeg_2] eq 'or' ) {
+                            next
+                              unless (
+                                $this_line_is_semicolon_terminated
+                                && (
+                                    $type_ibeg_1 eq '}'
+                                    || (
+
+                                        # following 'if' or 'unless' or 'or'
+                                        $type_ibeg_1 eq 'k'
+                                        && $is_if_unless{ $tokens_to_go[$ibeg_1]
+                                        }
+
+                                        # important: only combine a very simple
+                                        # or statement because the step below
+                                        # may have combined a trailing 'and'
+                                        # with this or, and we do not want to
+                                        # then combine everything together
+                                        && ( $iend_2 - $ibeg_2 <= 7 )
+                                    )
+                                )
+                              );
+
+                            #X: RT #81854
+                            $forced_breakpoint_to_go[$iend_1] = 0
+                              unless ( $old_breakpoint_to_go[$iend_1] );
+                        }
+
+                        # handle leading 'and' and 'xor'
+                        elsif ($tokens_to_go[$ibeg_2] eq 'and'
+                            || $tokens_to_go[$ibeg_2] eq 'xor' )
+                        {
+
+                            # Decide if we will combine a single terminal 'and'
+                            # after an 'if' or 'unless'.
+
+                            #     This looks best with the 'and' on the same
+                            #     line as the 'if':
+                            #
+                            #         $a = 1
+                            #           if $seconds and $nu < 2;
+                            #
+                            #     But this looks better as shown:
+                            #
+                            #         $a = 1
+                            #           if !$this->{Parents}{$_}
+                            #           or $this->{Parents}{$_} eq $_;
+                            #
+                            next
+                              unless (
+                                $this_line_is_semicolon_terminated
+                                && (
 
                                     # following 'if' or 'unless' or 'or'
                                     $type_ibeg_1 eq 'k'
-                                    && $is_if_unless{ $tokens_to_go[$ibeg_1] }
-
-                                    # important: only combine a very simple or
-                                    # statement because the step below may have
-                                    # combined a trailing 'and' with this or,
-                                    # and we do not want to then combine
-                                    # everything together
-                                    && ( $iend_2 - $ibeg_2 <= 7 )
+                                    && ( $is_if_unless{ $tokens_to_go[$ibeg_1] }
+                                        || $tokens_to_go[$ibeg_1] eq 'or' )
                                 )
-                            )
-                          );
+                              );
+                        }
 
-                        #X: RT #81854
-                        $forced_breakpoint_to_go[$iend_1] = 0
-                          unless $old_breakpoint_to_go[$iend_1];
-                    }
+                        # handle leading "if" and "unless"
+                        elsif ( $is_if_unless{ $tokens_to_go[$ibeg_2] } ) {
 
-                    # handle leading 'and' and 'xor'
-                    elsif ($tokens_to_go[$ibeg_2] eq 'and'
-                        || $tokens_to_go[$ibeg_2] eq 'xor' )
-                    {
+                            # Combine something like:
+                            #    next
+                            #      if ( $lang !~ /${l}$/i );
+                            # into:
+                            #    next if ( $lang !~ /${l}$/i );
+                            next
+                              unless (
+                                $this_line_is_semicolon_terminated
 
-                        # Decide if we will combine a single terminal 'and'
-                        # after an 'if' or 'unless'.
+                                #  previous line begins with 'and' or 'or'
+                                && $type_ibeg_1 eq 'k'
+                                && $is_and_or{ $tokens_to_go[$ibeg_1] }
 
-                        #     This looks best with the 'and' on the same
-                        #     line as the 'if':
-                        #
-                        #         $a = 1
-                        #           if $seconds and $nu < 2;
-                        #
-                        #     But this looks better as shown:
-                        #
-                        #         $a = 1
-                        #           if !$this->{Parents}{$_}
-                        #           or $this->{Parents}{$_} eq $_;
-                        #
-                        next
-                          unless (
-                            $this_line_is_semicolon_terminated
-                            && (
+                              );
+                        }
 
-                                # following 'if' or 'unless' or 'or'
-                                $type_ibeg_1 eq 'k'
-                                && (   $is_if_unless{ $tokens_to_go[$ibeg_1] }
-                                    || $tokens_to_go[$ibeg_1] eq 'or' )
-                            )
-                          );
+                        # handle all other leading keywords
+                        else {
+
+                            # keywords look best at start of lines,
+                            # but combine things like "1 while"
+                            unless ( $is_assignment{$type_iend_1} ) {
+                                next
+                                  if ( ( $type_iend_1 ne 'k' )
+                                    && ( $tokens_to_go[$ibeg_2] ne 'while' ) );
+                            }
+                        }
                     }
 
-                    # handle leading "if" and "unless"
-                    elsif ( $is_if_unless{ $tokens_to_go[$ibeg_2] } ) {
+                    # similar treatment of && and || as above for 'and' and
+                    # 'or': NOTE: This block of code is currently bypassed
+                    # because of a previous block but is retained for possible
+                    # future use.
+                    elsif ( $is_amp_amp{$type_ibeg_2} ) {
+
+                        # maybe looking at something like:
+                        # unless $TEXTONLY || $item =~ m%</?(hr>|p>|a|img)%i;
 
-                        # Combine something like:
-                        #    next
-                        #      if ( $lang !~ /${l}$/i );
-                        # into:
-                        #    next if ( $lang !~ /${l}$/i );
                         next
                           unless (
                             $this_line_is_semicolon_terminated
 
-                            #  previous line begins with 'and' or 'or'
+                            # previous line begins with an 'if' or 'unless'
+                            # keyword
                             && $type_ibeg_1 eq 'k'
-                            && $is_and_or{ $tokens_to_go[$ibeg_1] }
+                            && $is_if_unless{ $tokens_to_go[$ibeg_1] }
 
                           );
                     }
 
-                    # handle all other leading keywords
-                    else {
-
-                        # keywords look best at start of lines,
-                        # but combine things like "1 while"
-                        unless ( $is_assignment{$type_iend_1} ) {
-                            next
-                              if ( ( $type_iend_1 ne 'k' )
-                                && ( $tokens_to_go[$ibeg_2] ne 'while' ) );
-                        }
-                    }
-                }
-
-                # similar treatment of && and || as above for 'and' and 'or':
-                # NOTE: This block of code is currently bypassed because
-                # of a previous block but is retained for possible future use.
-                elsif ( $is_amp_amp{$type_ibeg_2} ) {
-
-                    # maybe looking at something like:
-                    # unless $TEXTONLY || $item =~ m%</?(hr>|p>|a|img)%i;
-
-                    next
-                      unless (
-                        $this_line_is_semicolon_terminated
-
-                        # previous line begins with an 'if' or 'unless' keyword
-                        && $type_ibeg_1 eq 'k'
-                        && $is_if_unless{ $tokens_to_go[$ibeg_1] }
-
-                      );
-                }
-
-                # handle line with leading = or similar
-                elsif ( $is_assignment{$type_ibeg_2} ) {
-                    next unless ( $n == 1 || $n == $nmax );
-                    next if $old_breakpoint_to_go[$iend_1];
-                    next
-                      unless (
+                    # handle line with leading = or similar
+                    elsif ( $is_assignment{$type_ibeg_2} ) {
+                        next unless ( $n == 1 || $n == $nmax );
+                        next if ( $old_breakpoint_to_go[$iend_1] );
+                        next
+                          unless (
 
-                        # unless we can reduce this to two lines
-                        $nmax == 2
+                            # unless we can reduce this to two lines
+                            $nmax == 2
 
-                        # or three lines, the last with a leading semicolon
-                        || ( $nmax == 3 && $types_to_go[$ibeg_nmax] eq ';' )
+                            # or three lines, the last with a leading semicolon
+                            || ( $nmax == 3 && $types_to_go[$ibeg_nmax] eq ';' )
 
-                        # or the next line ends with a here doc
-                        || $type_iend_2 eq 'h'
+                            # or the next line ends with a here doc
+                            || $type_iend_2 eq 'h'
 
-                        # or this is a short line ending in ;
-                        || ( $n == $nmax && $this_line_is_semicolon_terminated )
-                      );
-                    $forced_breakpoint_to_go[$iend_1] = 0;
-                }
+                            # or this is a short line ending in ;
+                            || (   $n == $nmax
+                                && $this_line_is_semicolon_terminated )
+                          );
+                        $forced_breakpoint_to_go[$iend_1] = 0;
+                    }
 
-                #----------------------------------------------------------
-                # Recombine Section 4:
-                # Combine the lines if we arrive here and it is possible
-                #----------------------------------------------------------
+                    #----------------------------------------------------------
+                    # Recombine Section 4:
+                    # Combine the lines if we arrive here and it is possible
+                    #----------------------------------------------------------
 
-                # honor hard breakpoints
-                next if ( $forced_breakpoint_to_go[$iend_1] > 0 );
+                    # honor hard breakpoints
+                    next if ( $forced_breakpoint_to_go[$iend_1] > 0 );
 
-                my $bs = $bond_strength_to_go[$iend_1] + $bs_tweak;
+                    my $bs = $bond_strength_to_go[$iend_1] + $bs_tweak;
 
-                # Require a few extra spaces before recombining lines if we are
-                # at an old breakpoint unless this is a simple list or terminal
-                # line.  The goal is to avoid oscillating between two
-                # quasi-stable end states.  For example this snippet caused
-                # problems:
+                 # Require a few extra spaces before recombining lines if we are
+                 # at an old breakpoint unless this is a simple list or terminal
+                 # line.  The goal is to avoid oscillating between two
+                 # quasi-stable end states.  For example this snippet caused
+                 # problems:
 ##    my $this =
 ##    bless {
 ##        TText => "[" . ( join ',', map { "\"$_\"" } split "\n", $_ ) . "]"
 ##      },
 ##      $type;
-                next
-                  if ( $old_breakpoint_to_go[$iend_1]
-                    && !$this_line_is_semicolon_terminated
-                    && $n < $nmax
-                    && $excess + 4 > 0
-                    && $type_iend_2 ne ',' );
-
-                # do not recombine if we would skip in indentation levels
-                if ( $n < $nmax ) {
-                    my $if_next = $ri_beg->[ $n + 1 ];
                     next
-                      if (
-                           $levels_to_go[$ibeg_1] < $levels_to_go[$ibeg_2]
-                        && $levels_to_go[$ibeg_2] < $levels_to_go[$if_next]
-
-                        # but an isolated 'if (' is undesirable
-                        && !(
-                               $n == 1
-                            && $iend_1 - $ibeg_1 <= 2
-                            && $type_ibeg_1 eq 'k'
-                            && $tokens_to_go[$ibeg_1] eq 'if'
-                            && $tokens_to_go[$iend_1] ne '('
-                        )
-                      );
-                }
+                      if ( $old_breakpoint_to_go[$iend_1]
+                        && !$this_line_is_semicolon_terminated
+                        && $n < $nmax
+                        && $excess + 4 > 0
+                        && $type_iend_2 ne ',' );
 
-                # honor no-break's
-                next if ( $bs >= NO_BREAK - 1 );
+                    # do not recombine if we would skip in indentation levels
+                    if ( $n < $nmax ) {
+                        my $if_next = $ri_beg->[ $n + 1 ];
+                        next
+                          if (
+                               $levels_to_go[$ibeg_1] < $levels_to_go[$ibeg_2]
+                            && $levels_to_go[$ibeg_2] < $levels_to_go[$if_next]
+
+                            # but an isolated 'if (' is undesirable
+                            && !(
+                                   $n == 1
+                                && $iend_1 - $ibeg_1 <= 2
+                                && $type_ibeg_1 eq 'k'
+                                && $tokens_to_go[$ibeg_1] eq 'if'
+                                && $tokens_to_go[$iend_1] ne '('
+                            )
+                          );
+                    }
 
-                # remember the pair with the greatest bond strength
-                if ( !$n_best ) {
-                    $n_best  = $n;
-                    $bs_best = $bs;
-                }
-                else {
+                    # honor no-break's
+                    ## next if ( $bs >= NO_BREAK - 1 );  # removed for b1257
 
-                    if ( $bs > $bs_best ) {
+                    # remember the pair with the greatest bond strength
+                    if ( !$n_best ) {
                         $n_best  = $n;
                         $bs_best = $bs;
                     }
+                    else {
+
+                        if ( $bs > $bs_best ) {
+                            $n_best  = $n;
+                            $bs_best = $bs;
+                        }
+                    }
                 }
-            }
 
-            # recombine the pair with the greatest bond strength
-            if ($n_best) {
-                splice @{$ri_beg}, $n_best, 1;
-                splice @{$ri_end}, $n_best - 1, 1;
-                splice @joint, $n_best, 1;
+                # recombine the pair with the greatest bond strength
+                if ($n_best) {
+                    splice @{$ri_beg}, $n_best,     1;
+                    splice @{$ri_end}, $n_best - 1, 1;
+                    splice @joint,     $n_best,     1;
 
-                # keep going if we are still making progress
-                $more_to_do++;
-            }
+                    # keep going if we are still making progress
+                    $more_to_do++;
+                }
+            }    # end iteration loop
+
+        }    # end loop over sections
+
+      RETURN:
+
+        if (DEBUG_RECOMBINE) {
+            my $nmax = @{$ri_end} - 1;
+            print STDERR
+"exiting recombine with $nmax lines, starting lines=$nmax_start, iterations=$it_count, max_it=$it_count_max numsec=$num_sections\n";
         }
-        return ( $ri_beg, $ri_end );
+        return;
     }
 } ## end closure recombine_breakpoints
 
@@ -14577,6 +16940,8 @@ sub note_embedded_tab {
     return;
 }
 
+use constant DEBUG_CORRECT_LP => 0;
+
 sub correct_lp_indentation {
 
     # When the -lp option is used, we need to make a last pass through
@@ -14587,7 +16952,9 @@ sub correct_lp_indentation {
     # tries to patch things up once the actual opening paren locations
     # are known.
     my ( $self, $ri_first, $ri_last ) = @_;
-    my $do_not_pad = 0;
+    my $K_opening_container = $self->[_K_opening_container_];
+    my $K_closing_container = $self->[_K_closing_container_];
+    my $do_not_pad          = 0;
 
     #  Note on flag '$do_not_pad':
     #  We want to avoid a situation like this, where the aligner inserts
@@ -14606,69 +16973,131 @@ sub correct_lp_indentation {
     #  We leave it to the aligner to decide how to do this.
 
     # first remove continuation indentation if appropriate
+    my $rLL      = $self->[_rLL_];
     my $max_line = @{$ri_first} - 1;
 
-    # looking at each line of this batch..
+    #---------------------------------------------------------------------------
+    # PASS 1: reduce indentation if necessary at any long one-line blocks (c098)
+    #---------------------------------------------------------------------------
+
+    # The point is that sub 'starting_one_line_block' made one-line blocks based
+    # on default indentation, not -lp indentation. So some of the one-line
+    # blocks may be too long when given -lp indentation.  We will fix that now
+    # if possible, using the list of these closing block indexes.
+    my $ri_starting_one_line_block =
+      $self->[_this_batch_]->[_ri_starting_one_line_block_];
+    if ( @{$ri_starting_one_line_block} ) {
+        my @ilist = @{$ri_starting_one_line_block};
+        my $inext = shift(@ilist);
+
+        # loop over lines, checking length of each with a one-line block
+        my ( $ibeg, $iend );
+        foreach my $line ( 0 .. $max_line ) {
+            $iend = $ri_last->[$line];
+            next if ( $inext > $iend );
+            $ibeg = $ri_first->[$line];
+
+            # This is just for lines with indentation objects (c098)
+            my $excess =
+              ref( $leading_spaces_to_go[$ibeg] )
+              ? $self->excess_line_length( $ibeg, $iend )
+              : 0;
+
+            if ( $excess > 0 ) {
+                my $available_spaces = $self->get_available_spaces_to_go($ibeg);
+
+                if ( $available_spaces > 0 ) {
+                    my $delete_want = min( $available_spaces, $excess );
+                    my $deleted_spaces =
+                      $self->reduce_lp_indentation( $ibeg, $delete_want );
+                    $available_spaces =
+                      $self->get_available_spaces_to_go($ibeg);
+                }
+            }
+
+            # skip forward to next one-line block to check
+            while (@ilist) {
+                $inext = shift @ilist;
+                next if ( $inext <= $iend );
+                last if ( $inext > $iend );
+            }
+            last if ( $inext <= $iend );
+        }
+    }
+
+    #-------------------------------------------------------------------
+    # PASS 2: look for and fix other problems in each line of this batch
+    #-------------------------------------------------------------------
+
+    # look at each output line ...
     my ( $ibeg, $iend );
     foreach my $line ( 0 .. $max_line ) {
         $ibeg = $ri_first->[$line];
         $iend = $ri_last->[$line];
 
-        # looking at each token in this output line..
+        # looking at each token in this output line ...
         foreach my $i ( $ibeg .. $iend ) {
 
             # How many space characters to place before this token
             # for special alignment.  Actual padding is done in the
             # continue block.
 
-            # looking for next unvisited indentation item
+            # looking for next unvisited indentation item ...
             my $indentation = $leading_spaces_to_go[$i];
-            if ( !$indentation->get_marked() ) {
-                $indentation->set_marked(1);
-
-                # looking for indentation item for which we are aligning
-                # with parens, braces, and brackets
-                next unless ( $indentation->get_align_paren() );
-
-                # skip closed container on this line
-                if ( $i > $ibeg ) {
-                    my $im = max( $ibeg, $iprev_to_go[$i] );
-                    if (   $type_sequence_to_go[$im]
-                        && $mate_index_to_go[$im] <= $iend )
-                    {
-                        next;
-                    }
-                }
 
-                if ( $line == 1 && $i == $ibeg ) {
-                    $do_not_pad = 1;
-                }
+            # This is just for indentation objects (c098)
+            next unless ( ref($indentation) );
 
-                # Ok, let's see what the error is and try to fix it
-                my $actual_pos;
-                my $predicted_pos = $indentation->get_spaces();
-                if ( $i > $ibeg ) {
+            # Visit each indentation object just once
+            next if ( $indentation->get_marked() );
 
-                    # token is mid-line - use length to previous token
-                    $actual_pos = total_line_length( $ibeg, $i - 1 );
+            # Mark first visit
+            $indentation->set_marked(1);
 
-                    # for mid-line token, we must check to see if all
-                    # additional lines have continuation indentation,
-                    # and remove it if so.  Otherwise, we do not get
-                    # good alignment.
-                    my $closing_index = $indentation->get_closed();
-                    if ( $closing_index > $iend ) {
-                        my $ibeg_next = $ri_first->[ $line + 1 ];
-                        if ( $ci_levels_to_go[$ibeg_next] > 0 ) {
-                            $self->undo_lp_ci( $line, $i, $closing_index,
-                                $ri_first, $ri_last );
-                        }
+            # Skip indentation objects which do not align with container tokens
+            my $align_seqno = $indentation->get_align_seqno();
+            next unless ($align_seqno);
+
+            # Skip a container which is entirely on this line
+            my $Ko = $K_opening_container->{$align_seqno};
+            my $Kc = $K_closing_container->{$align_seqno};
+            if ( defined($Ko) && defined($Kc) ) {
+                next if ( $Ko >= $K_to_go[$ibeg] && $Kc <= $K_to_go[$iend] );
+            }
+
+            if ( $line == 1 && $i == $ibeg ) {
+                $do_not_pad = 1;
+            }
+
+            #--------------------------------------------
+            # Now see what the error is and try to fix it
+            #--------------------------------------------
+            my $closing_index = $indentation->get_closed();
+            my $predicted_pos = $indentation->get_spaces();
+
+            # Find actual position:
+            my $actual_pos;
+
+            if ( $i == $ibeg ) {
+
+                # Case 1: token is first character of of batch - table lookup
+                if ( $line == 0 ) {
+
+                    $actual_pos = $predicted_pos;
+
+                    my ( $indent, $offset, $is_leading, $exists ) =
+                      get_saved_opening_indentation($align_seqno);
+                    if ( defined($indent) ) {
+
+                        # FIXME: should use '1' here if no space after opening
+                        # and '2' if want space; hardwired at 1 like -gnu-style
+                        $actual_pos = get_spaces($indent) + $offset + 1;
                     }
                 }
-                elsif ( $line > 0 ) {
 
-                    # handle case where token starts a new line;
-                    # use length of previous line
+                # Case 2: token starts a new line - use length of previous line
+                else {
+
                     my $ibegm = $ri_first->[ $line - 1 ];
                     my $iendm = $ri_last->[ $line - 1 ];
                     $actual_pos = total_line_length( $ibegm, $iendm );
@@ -14676,125 +17105,178 @@ sub correct_lp_indentation {
                     # follow -pt style
                     ++$actual_pos
                       if ( $types_to_go[ $iendm + 1 ] eq 'b' );
-                }
-                else {
 
-                    # token is first character of first line of batch
-                    $actual_pos = $predicted_pos;
                 }
+            }
 
-                my $move_right = $actual_pos - $predicted_pos;
+            # Case 3: $i>$ibeg: token is mid-line - use length to previous token
+            else {
 
-                # done if no error to correct (gnu2.t)
-                if ( $move_right == 0 ) {
-                    $indentation->set_recoverable_spaces($move_right);
-                    next;
+                $actual_pos = total_line_length( $ibeg, $i - 1 );
+
+                # for mid-line token, we must check to see if all
+                # additional lines have continuation indentation,
+                # and remove it if so.  Otherwise, we do not get
+                # good alignment.
+                if ( $closing_index > $iend ) {
+                    my $ibeg_next = $ri_first->[ $line + 1 ];
+                    if ( $ci_levels_to_go[$ibeg_next] > 0 ) {
+                        $self->undo_lp_ci( $line, $i, $closing_index,
+                            $ri_first, $ri_last );
+                    }
                 }
+            }
 
-                # if we have not seen closure for this indentation in
-                # this batch, we can only pass on a request to the
-                # vertical aligner
-                my $closing_index = $indentation->get_closed();
+            # By how many spaces (plus or minus) would we need to increase the
+            # indentation to get alignment with the opening token?
+            my $move_right = $actual_pos - $predicted_pos;
 
-                if ( $closing_index < 0 ) {
-                    $indentation->set_recoverable_spaces($move_right);
-                    next;
+            if (DEBUG_CORRECT_LP) {
+                my $tok   = substr( $tokens_to_go[$i], 0, 8 );
+                my $avail = $self->get_available_spaces_to_go($ibeg);
+                print
+"CORRECT_LP for seq=$align_seqno, predicted pos=$predicted_pos actual=$actual_pos => move right=$move_right available=$avail i=$i max=$max_index_to_go tok=$tok\n";
+            }
+
+            # nothing more to do if no error to correct (gnu2.t)
+            if ( $move_right == 0 ) {
+                $indentation->set_recoverable_spaces($move_right);
+                next;
+            }
+
+            # Get any collapsed length defined for -xlp
+            my $collapsed_length =
+              $self->[_rcollapsed_length_by_seqno_]->{$align_seqno};
+            $collapsed_length = 0 unless ( defined($collapsed_length) );
+
+            if (DEBUG_CORRECT_LP) {
+                print
+"CORRECT_LP for seq=$align_seqno, collapsed length is $collapsed_length\n";
+            }
+
+            # if we have not seen closure for this indentation in this batch,
+            # and do not have a collapsed length estimate, we can only pass on
+            # a request to the vertical aligner
+            if ( $closing_index < 0 && !$collapsed_length ) {
+                $indentation->set_recoverable_spaces($move_right);
+                next;
+            }
+
+            # If necessary, look ahead to see if there is really any leading
+            # whitespace dependent on this whitespace, and also find the
+            # longest line using this whitespace.  Since it is always safe to
+            # move left if there are no dependents, we only need to do this if
+            # we may have dependent nodes or need to move right.
+
+            my $have_child = $indentation->get_have_child();
+            my %saw_indentation;
+            my $line_count = 1;
+            $saw_indentation{$indentation} = $indentation;
+
+            # How far can we move right before we hit the limit?
+            # let $right_margen = the number of spaces that we can increase
+            # the current indentation before hitting the maximum line length.
+            my $right_margin = 0;
+
+            if ( $have_child || $move_right > 0 ) {
+                $have_child = 0;
+
+                # include estimated collapsed length for incomplete containers
+                my $max_length = 0;
+                if ( $Kc > $K_to_go[$max_index_to_go] ) {
+                    $max_length = $collapsed_length + $predicted_pos;
                 }
 
-                # If necessary, look ahead to see if there is really any
-                # leading whitespace dependent on this whitespace, and
-                # also find the longest line using this whitespace.
-                # Since it is always safe to move left if there are no
-                # dependents, we only need to do this if we may have
-                # dependent nodes or need to move right.
+                if ( $i == $ibeg ) {
+                    my $length = total_line_length( $ibeg, $iend );
+                    if ( $length > $max_length ) { $max_length = $length }
+                }
 
-                my $right_margin = 0;
-                my $have_child   = $indentation->get_have_child();
+                # look ahead at the rest of the lines of this batch..
+                foreach my $line_t ( $line + 1 .. $max_line ) {
+                    my $ibeg_t = $ri_first->[$line_t];
+                    my $iend_t = $ri_last->[$line_t];
+                    last if ( $closing_index <= $ibeg_t );
 
-                my %saw_indentation;
-                my $line_count = 1;
-                $saw_indentation{$indentation} = $indentation;
+                    # remember all different indentation objects
+                    my $indentation_t = $leading_spaces_to_go[$ibeg_t];
+                    $saw_indentation{$indentation_t} = $indentation_t;
+                    $line_count++;
 
-                if ( $have_child || $move_right > 0 ) {
-                    $have_child = 0;
-                    my $max_length = 0;
-                    if ( $i == $ibeg ) {
-                        $max_length = total_line_length( $ibeg, $iend );
+                    # remember longest line in the group
+                    my $length_t = total_line_length( $ibeg_t, $iend_t );
+                    if ( $length_t > $max_length ) {
+                        $max_length = $length_t;
                     }
+                }
 
-                    # look ahead at the rest of the lines of this batch..
-                    foreach my $line_t ( $line + 1 .. $max_line ) {
-                        my $ibeg_t = $ri_first->[$line_t];
-                        my $iend_t = $ri_last->[$line_t];
-                        last if ( $closing_index <= $ibeg_t );
-
-                        # remember all different indentation objects
-                        my $indentation_t = $leading_spaces_to_go[$ibeg_t];
-                        $saw_indentation{$indentation_t} = $indentation_t;
-                        $line_count++;
-
-                        # remember longest line in the group
-                        my $length_t = total_line_length( $ibeg_t, $iend_t );
-                        if ( $length_t > $max_length ) {
-                            $max_length = $length_t;
-                        }
-                    }
-                    $right_margin =
-                      $maximum_line_length_at_level[ $levels_to_go[$ibeg] ] -
-                      $max_length;
-                    if ( $right_margin < 0 ) { $right_margin = 0 }
-                }
-
-                my $first_line_comma_count =
-                  grep { $_ eq ',' } @types_to_go[ $ibeg .. $iend ];
-                my $comma_count = $indentation->get_comma_count();
-                my $arrow_count = $indentation->get_arrow_count();
-
-                # This is a simple approximate test for vertical alignment:
-                # if we broke just after an opening paren, brace, bracket,
-                # and there are 2 or more commas in the first line,
-                # and there are no '=>'s,
-                # then we are probably vertically aligned.  We could set
-                # an exact flag in sub scan_list, but this is good
-                # enough.
-                my $indentation_count = keys %saw_indentation;
-                my $is_vertically_aligned =
-                  (      $i == $ibeg
-                      && $first_line_comma_count > 1
-                      && $indentation_count == 1
-                      && ( $arrow_count == 0 || $arrow_count == $line_count ) );
-
-                # Make the move if possible ..
-                if (
+                $right_margin =
+                  $maximum_line_length_at_level[ $levels_to_go[$ibeg] ] -
+                  $max_length;
+                if ( $right_margin < 0 ) { $right_margin = 0 }
+            }
+
+            my $first_line_comma_count =
+              grep { $_ eq ',' } @types_to_go[ $ibeg .. $iend ];
+            my $comma_count = $indentation->get_comma_count();
+            my $arrow_count = $indentation->get_arrow_count();
+
+            # This is a simple approximate test for vertical alignment:
+            # if we broke just after an opening paren, brace, bracket,
+            # and there are 2 or more commas in the first line,
+            # and there are no '=>'s,
+            # then we are probably vertically aligned.  We could set
+            # an exact flag in sub break_lists, but this is good
+            # enough.
+            my $indentation_count = keys %saw_indentation;
+            my $is_vertically_aligned =
+              (      $i == $ibeg
+                  && $first_line_comma_count > 1
+                  && $indentation_count == 1
+                  && ( $arrow_count == 0 || $arrow_count == $line_count ) );
+
+            # Make the move if possible ..
+            if (
 
-                    # we can always move left
-                    $move_right < 0
+                # we can always move left
+                $move_right < 0
 
-                    # but we should only move right if we are sure it will
-                    # not spoil vertical alignment
-                    || ( $comma_count == 0 )
-                    || ( $comma_count > 0 && !$is_vertically_aligned )
-                  )
-                {
-                    my $move =
-                      ( $move_right <= $right_margin )
-                      ? $move_right
-                      : $right_margin;
-
-                    foreach ( keys %saw_indentation ) {
-                        $saw_indentation{$_}
-                          ->permanently_decrease_available_spaces( -$move );
-                    }
+                # -xlp
+
+                # incomplete container
+                || (   $rOpts_extended_line_up_parentheses
+                    && $Kc > $K_to_go[$max_index_to_go] )
+                || $closing_index < 0
+
+                # but we should only move right if we are sure it will
+                # not spoil vertical alignment
+                || ( $comma_count == 0 )
+                || ( $comma_count > 0 && !$is_vertically_aligned )
+              )
+            {
+                my $move =
+                  ( $move_right <= $right_margin )
+                  ? $move_right
+                  : $right_margin;
+
+                if (DEBUG_CORRECT_LP) {
+                    print
+                      "CORRECT_LP for seq=$align_seqno, moving $move spaces\n";
                 }
 
-                # Otherwise, record what we want and the vertical aligner
-                # will try to recover it.
-                else {
-                    $indentation->set_recoverable_spaces($move_right);
+                foreach ( keys %saw_indentation ) {
+                    $saw_indentation{$_}
+                      ->permanently_decrease_available_spaces( -$move );
                 }
             }
-        }
-    }
+
+            # Otherwise, record what we want and the vertical aligner
+            # will try to recover it.
+            else {
+                $indentation->set_recoverable_spaces($move_right);
+            }
+        } ## end loop over tokens in a line
+    } ## end loop over lines
     return $do_not_pad;
 }
 
@@ -14851,9 +17333,12 @@ sub undo_lp_ci {
 # CODE SECTION 10: Code to break long statments
 ###############################################
 
-sub set_continuation_breaks {
+sub break_long_lines {
 
-    # Called once per batch to set breaks in long lines.
+    #-----------------------------------------------------------
+    # Break a batch of tokens into lines which do not exceed the
+    # maximum line length.
+    #-----------------------------------------------------------
 
     # Define an array of indexes for inserting newline characters to
     # keep the line lengths below the maximum desired length.  There is
@@ -14863,7 +17348,7 @@ sub set_continuation_breaks {
     # This routine is part of series of routines which adjust line
     # lengths.  It is only called if a statement is longer than the
     # maximum line length, or if a preliminary scanning located
-    # desirable break points.   Sub scan_list has already looked at
+    # desirable break points.   Sub break_lists has already looked at
     # these tokens and set breakpoints (in array
     # $forced_breakpoint_to_go[$i]) where it wants breaks (for example
     # after commas, after opening parens, and before closing parens).
@@ -14888,7 +17373,7 @@ sub set_continuation_breaks {
     # @{$rcolon_list} is a list of all the ? and : tokens in the batch, in
     # order.
 
-    use constant DEBUG_BREAKPOINTS => 0;
+    use constant DEBUG_BREAK_LINES => 0;
 
     my @i_first        = ();    # the first index to output
     my @i_last         = ();    # the last index to output
@@ -14940,6 +17425,18 @@ sub set_continuation_breaks {
         my $maximum_line_length =
           $maximum_line_length_at_level[ $levels_to_go[$i_begin] ];
 
+        # Do not separate an isolated bare word from an opening paren.
+        # Alternate Fix #2 for issue b1299.  This waits as long as possible
+        # to make the decision.
+        if ( $types_to_go[$i_begin] eq 'i'
+            && substr( $tokens_to_go[$i_begin], 0, 1 ) =~ /\w/ )
+        {
+            my $i_next_nonblank = $inext_to_go[$i_begin];
+            if ( $tokens_to_go[$i_next_nonblank] eq '(' ) {
+                $bond_strength_to_go[$i_begin] = NO_BREAK;
+            }
+        }
+
         #-------------------------------------------------------
         # BEGINNING of inner loop to find the best next breakpoint
         #-------------------------------------------------------
@@ -14983,7 +17480,7 @@ sub set_continuation_breaks {
               )
             {
                 $strength -= $tiny_bias;
-                DEBUG_BREAKPOINTS && do { $Msg .= " :-bias at i=$i_test" };
+                DEBUG_BREAK_LINES && do { $Msg .= " :-bias at i=$i_test" };
             }
 
             # otherwise increase strength a bit if this token would be at the
@@ -14996,7 +17493,7 @@ sub set_continuation_breaks {
                   $starting_sum;
                 if ( $len >= $maximum_line_length ) {
                     $strength += $tiny_bias;
-                    DEBUG_BREAKPOINTS && do { $Msg .= " :+bias at i=$i_test" };
+                    DEBUG_BREAK_LINES && do { $Msg .= " :+bias at i=$i_test" };
                 }
             }
 
@@ -15006,9 +17503,9 @@ sub set_continuation_breaks {
             # with lower level than the start of the line,
             # unless we've already seen a better break.
             #
-            ##############################################
+            #------------------------------------
             # Note on an issue with a preceding ?
-            ##############################################
+            #------------------------------------
             # We don't include a ? in the above list, but there may
             # be a break at a previous ? if the line is long.
             # Because of this we do not want to force a break if
@@ -15035,18 +17532,18 @@ sub set_continuation_breaks {
               )
             {
                 $self->set_forced_breakpoint($i_next_nonblank);
-                DEBUG_BREAKPOINTS
+                DEBUG_BREAK_LINES
                   && do { $Msg .= " :Forced break at i=$i_next_nonblank" };
             }
 
             if (
 
-                # Try to put a break where requested by scan_list
+                # Try to put a break where requested by break_lists
                 $forced_breakpoint_to_go[$i_test]
 
                 # break between ) { in a continued line so that the '{' can
                 # be outdented
-                # See similar logic in scan_list which catches instances
+                # See similar logic in break_lists which catches instances
                 # where a line is just something like ') {'.  We have to
                 # be careful because the corresponding block keyword might
                 # not be on the first line, such as 'for' here:
@@ -15068,12 +17565,16 @@ sub set_continuation_breaks {
                     # sub block breaks handled at higher level, unless
                     # it looks like the preceding list is long and broken
                     && !(
-                        $next_nonblank_block_type =~ /$ANYSUB_PATTERN/
+
+                        (
+                               $next_nonblank_block_type =~ /$SUB_PATTERN/
+                            || $next_nonblank_block_type =~ /$ASUB_PATTERN/
+                        )
                         && ( $nesting_depth_to_go[$i_begin] ==
                             $nesting_depth_to_go[$i_next_nonblank] )
                     )
 
-                    && !$rOpts->{'opening-brace-always-on-right'}
+                    && !$rOpts_opening_brace_always_on_right
                 )
 
                 # There is an implied forced break at a terminal opening brace
@@ -15087,7 +17588,7 @@ sub set_continuation_breaks {
                 if ( $strength < NO_BREAK - 1 ) {
                     $strength   = $lowest_strength - $tiny_bias;
                     $must_break = 1;
-                    DEBUG_BREAKPOINTS
+                    DEBUG_BREAK_LINES
                       && do { $Msg .= " :set must_break at i=$i_next_nonblank" };
                 }
             }
@@ -15107,7 +17608,7 @@ sub set_continuation_breaks {
               )
             {
                 if ( $i_lowest >= 0 ) {
-                    DEBUG_BREAKPOINTS && do {
+                    DEBUG_BREAK_LINES && do {
                         $Msg .= " :quit at good terminal='$next_nonblank_type'";
                     };
                     last;
@@ -15134,7 +17635,7 @@ sub set_continuation_breaks {
               )
             {
                 $i_test = min( $imax, $inext_to_go[$i_test] );
-                DEBUG_BREAKPOINTS && do {
+                DEBUG_BREAK_LINES && do {
                     $Msg .= " :redo at i=$i_test";
                 };
                 redo;
@@ -15147,7 +17648,7 @@ sub set_continuation_breaks {
                 # a leading alignment of certain common tokens, and it
                 # is different from the latest candidate break
                 if ($leading_alignment_type) {
-                    DEBUG_BREAKPOINTS && do {
+                    DEBUG_BREAK_LINES && do {
                         $Msg .=
 " :last at leading_alignment='$leading_alignment_type'";
                     };
@@ -15173,7 +17674,7 @@ sub set_continuation_breaks {
                   )
                 {
 
-                    DEBUG_BREAKPOINTS && do {
+                    DEBUG_BREAK_LINES && do {
                         $Msg .= " :last at good old break\n";
                     };
                     last;
@@ -15208,7 +17709,7 @@ sub set_continuation_breaks {
                     if (   $types_to_go[$il] =~ /^[\/\*\+\-\%]$/
                         || $types_to_go[$ir] =~ /^[\/\*\+\-\%]$/ )
                     {
-                        DEBUG_BREAKPOINTS && do {
+                        DEBUG_BREAK_LINES && do {
                             $Msg .= " :last-noskip_short";
                         };
                         last;
@@ -15222,7 +17723,7 @@ sub set_continuation_breaks {
                 $lowest_next_type       = $next_nonblank_type;
                 $i_lowest_next_nonblank = $i_next_nonblank;
                 if ($must_break) {
-                    DEBUG_BREAKPOINTS && do {
+                    DEBUG_BREAK_LINES && do {
                         $Msg .= " :last-must_break";
                     };
                     last;
@@ -15292,13 +17793,13 @@ sub set_continuation_breaks {
                     && !$is_closing_type{$next_nonblank_type} )
                 {
                     $too_long = $next_length >= $maximum_line_length;
-                    DEBUG_BREAKPOINTS && do {
+                    DEBUG_BREAK_LINES && do {
                         $Msg .= " :too_long=$too_long" if ($too_long);
                     }
                 }
             }
 
-            DEBUG_BREAKPOINTS && do {
+            DEBUG_BREAK_LINES && do {
                 my $ltok     = $token;
                 my $rtok     = $next_nonblank_token ? $next_nonblank_token : "";
                 my $i_testp2 = $i_test + 2;
@@ -15321,7 +17822,7 @@ sub set_continuation_breaks {
               )
             {
                 $too_long = 0;
-                DEBUG_BREAKPOINTS && do {
+                DEBUG_BREAK_LINES && do {
                     $Msg .= " :do_not_strand next='$next_nonblank_type'";
                 };
             }
@@ -15336,7 +17837,7 @@ sub set_continuation_breaks {
                 || $i_test == $imax
               )
             {
-                DEBUG_BREAKPOINTS && do {
+                DEBUG_BREAK_LINES && do {
                     $Msg .=
 " :Done-too_long=$too_long or i_lowest=$i_lowest or $i_test==imax";
                 };
@@ -15392,7 +17893,7 @@ sub set_continuation_breaks {
         $next_nonblank_type  = $types_to_go[$i_next_nonblank];
         $next_nonblank_token = $tokens_to_go[$i_next_nonblank];
 
-        DEBUG_BREAKPOINTS
+        DEBUG_BREAK_LINES
           && print STDOUT
 "BREAK: best is i = $i_lowest strength = $lowest_strength;\nReason>> $Msg\n";
         $Msg = "";
@@ -15400,7 +17901,7 @@ sub set_continuation_breaks {
         #-------------------------------------------------------
         # ?/: rule 2 : if we break at a '?', then break at its ':'
         #
-        # Note: this rule is also in sub scan_list to handle a break
+        # Note: this rule is also in sub break_lists to handle a break
         # at the start and end of a line (in case breaks are dictated
         # by side comments).
         #-------------------------------------------------------
@@ -15463,7 +17964,7 @@ sub set_continuation_breaks {
         # update indentation size
         if ( $i_begin <= $imax ) {
             $leading_spaces = leading_spaces_to_go($i_begin);
-            DEBUG_BREAKPOINTS
+            DEBUG_BREAK_LINES
               && print STDOUT
               "updating leading spaces to be $leading_spaces at i=$i_begin\n";
         }
@@ -15511,22 +18012,24 @@ sub set_continuation_breaks {
 # CODE SECTION 11: Code to break long lists
 ###########################################
 
-{    ## begin closure scan_list
+{    ## begin closure break_lists
 
     # These routines and variables are involved in finding good
     # places to break long lists.
 
+    use constant DEBUG_BREAK_LISTS => 0;
+
     my (
-        $block_type,               $current_depth,
-        $depth,                    $i,
-        $i_last_nonblank_token,    $last_colon_sequence_number,
-        $last_nonblank_token,      $last_nonblank_type,
-        $last_nonblank_block_type, $last_old_breakpoint_count,
-        $minimum_depth,            $next_nonblank_block_type,
-        $next_nonblank_token,      $next_nonblank_type,
-        $old_breakpoint_count,     $starting_breakpoint_count,
-        $starting_depth,           $token,
-        $type,                     $type_sequence,
+        $block_type,                $current_depth,
+        $depth,                     $i,
+        $i_last_nonblank_token,     $last_nonblank_token,
+        $last_nonblank_type,        $last_nonblank_block_type,
+        $last_old_breakpoint_count, $minimum_depth,
+        $next_nonblank_block_type,  $next_nonblank_token,
+        $next_nonblank_type,        $old_breakpoint_count,
+        $starting_breakpoint_count, $starting_depth,
+        $token,                     $type,
+        $type_sequence,
     );
 
     my (
@@ -15546,16 +18049,17 @@ sub set_continuation_breaks {
     my ( @has_broken_sublist, @dont_align, @want_comma_break );
 
     my $length_tol;
-    my $length_tol_boost;
+    my $lp_tol_boost;
+    my $list_stress_level;
 
-    sub initialize_scan_list {
+    sub initialize_break_lists {
         @dont_align         = ();
         @has_broken_sublist = ();
         @want_comma_break   = ();
 
-        ####################################################
+        #---------------------------------------------------
         # Set tolerances to prevent formatting instabilities
-        ####################################################
+        #---------------------------------------------------
 
         # Define tolerances to use when checking if closed
         # containers will fit on one line.  This is necessary to avoid
@@ -15588,25 +18092,33 @@ sub set_continuation_breaks {
         # 'find_token_starting_list' to go back before an initial blank space.
         # This fixed these three cases, and allowed the tolerances to be
         # reduced to continue to fix all other known cases of instability.
-        # This gives the current tolerance formulation (note that
-        # variable $length_tol_boost is always 0 now):
+        # This gives the current tolerance formulation.
+
+        $lp_tol_boost = 0;
 
-        $length_tol_boost = 0;
         if ($rOpts_line_up_parentheses) {
 
-            if ( $rOpts->{'extended-continuation-indentation'} ) {
-                $length_tol += 2;
-                $length_tol_boost = 0;    # was 1 for FIX2, 0 for FIX3
+            # boost tol for combination -lp -xci
+            if ($rOpts_extended_continuation_indentation) {
+                $lp_tol_boost = 2;
             }
+
+            # boost tol for combination -lp and any -vtc > 0, but only for
+            # non-list containers
             else {
-                $length_tol_boost = 0;    # was 3 for FIX2, 0 for FIX3
+                foreach ( keys %closing_vertical_tightness ) {
+                    next
+                      unless ( $closing_vertical_tightness{$_} );
+                    $lp_tol_boost = 1;    # Fixes B1193;
+                    last;
+                }
             }
         }
 
-        # The -xci option alone also needs a slightly larger tol for non-lists
-        elsif ( $rOpts->{'extended-continuation-indentation'} ) {
-            $length_tol_boost = 0;        # was 1 for FIX2, 0 for FIX3
-        }
+        # Define a level where list formatting becomes highly stressed and
+        # needs to be simplified. Introduced for case b1262.
+        $list_stress_level = min( $stress_level_alpha, $stress_level_beta + 2 );
+
         return;
     }
 
@@ -15659,6 +18171,13 @@ sub set_continuation_breaks {
         my $bp_count           = 0;
         my $do_not_break_apart = 0;
 
+        # Do not break a list unless there are some non-line-ending commas.
+        # This avoids getting different results with only non-essential commas,
+        # and fixes b1192.
+        my $seqno = $type_sequence_stack[$dd];
+        my $real_comma_count =
+          $seqno ? $self->[_rtype_count_by_seqno_]->{$seqno}->{','} : 1;
+
         # anything to do?
         if ( $item_count_stack[$dd] ) {
 
@@ -15668,7 +18187,7 @@ sub set_continuation_breaks {
             }
 
             # handle commas within containers...
-            else {
+            elsif ($real_comma_count) {
                 my $fbc = get_forced_breakpoint_count();
 
                 # always open comma lists not preceded by keywords,
@@ -15700,11 +18219,17 @@ sub set_continuation_breaks {
     }
 
     # These types are excluded at breakpoints to prevent blinking
-    my %is_uncontained_comma_break_excluded_type;
+    # Switched from excluded to included as part of fix for b1214
+    ##my %is_uncontained_comma_break_excluded_type;
+    my %is_uncontained_comma_break_included_type;
 
     BEGIN {
-        my @q = qw< L { ( [ ? : + - >;
-        @is_uncontained_comma_break_excluded_type{@q} = (1) x scalar(@q);
+        ##my @q = qw< L { ( [ ? : + - =~ >;
+        ##@is_uncontained_comma_break_excluded_type{@q} = (1) x scalar(@q);
+
+        my @q = qw< k R } ) ] Y Z U w i q Q .
+          = **= += *= &= <<= &&= -= /= |= >>= ||= //= .= %= ^= x=>;
+        @is_uncontained_comma_break_included_type{@q} = (1) x scalar(@q);
     }
 
     sub do_uncontained_comma_breaks {
@@ -15740,7 +18265,10 @@ sub set_continuation_breaks {
         # (3) NEW: there are one or more old comma breaks (see return example)
         # (4) the first comma is at the starting level ...
         #     ... fixes cases b064 b065 b068 b210 b747
-        #
+        # (5) the batch does not start with a ci>0 [ignore a ci change by -xci]
+        #     ... fixes b1220.  If ci>0 we are in the middle of a snippet,
+        #     maybe because -boc has been forcing out previous lines.
+
         # For example, we will follow the user and break after
         # 'print' in this snippet:
         #    print
@@ -15768,7 +18296,15 @@ sub set_continuation_breaks {
         #
         my $i_first_comma = $comma_index[$dd]->[0];
         my $level_comma   = $levels_to_go[$i_first_comma];
-        if (   $old_breakpoint_to_go[$i_first_comma]
+        my $ci_start      = $ci_levels_to_go[0];
+
+        # Here we want to use the value of ci before any -xci adjustment
+        if ( $ci_start && $rOpts_extended_continuation_indentation ) {
+            my $K0 = $K_to_go[0];
+            if ( $self->[_rseqno_controlling_my_ci_]->{$K0} ) { $ci_start = 0 }
+        }
+        if (  !$ci_start
+            && $old_breakpoint_to_go[$i_first_comma]
             && $level_comma == $levels_to_go[0] )
         {
             my $ibreak    = -1;
@@ -15785,25 +18321,33 @@ sub set_continuation_breaks {
             # Changed rule from multiple old commas to just one here:
             if ( $ibreak >= 0 && $obp_count == 1 && $old_comma_break_count > 0 )
             {
-                my $ibreakm = $ibreak;
-                $ibreakm-- if ( $types_to_go[$ibreakm] eq 'b' );
-                if ( $ibreakm >= 0 ) {
+                my $ibreak_m = $ibreak;
+                $ibreak_m-- if ( $types_to_go[$ibreak_m] eq 'b' );
+                if ( $ibreak_m >= 0 ) {
 
                     # In order to avoid blinkers we have to be fairly
                     # restrictive:
 
-                    # Rule 1: Do not to break before an opening token
-                    # Rule 2: avoid breaking at ternary operators
-                    # (see b931, which is similar to the above print example)
-                    # Rule 3: Do not break at chain operators to fix case b1119
-                    #  - The previous test was '$typem !~ /^[\(\{\[L\?\:]$/'
+                    # OLD Rules:
+                    #  Rule 1: Do not to break before an opening token
+                    #  Rule 2: avoid breaking at ternary operators
+                    #  (see b931, which is similar to the above print example)
+                    #  Rule 3: Do not break at chain operators to fix case b1119
+                    #   - The previous test was '$typem !~ /^[\(\{\[L\?\:]$/'
+
+                    # NEW Rule, replaced above rules after case b1214:
+                    #  only break at one of the included types
 
                     # Be sure to test any changes to these rules against runs
                     # with -l=0 such as the 'bbvt' test (perltidyrc_colin)
                     # series.
+                    my $type_m = $types_to_go[$ibreak_m];
 
-                    my $typem = $types_to_go[$ibreakm];
-                    if ( !$is_uncontained_comma_break_excluded_type{$typem} ) {
+                    # Switched from excluded to included for b1214. If necessary
+                    # the token could also be checked if type_m eq 'k'
+                    ##if ( !$is_uncontained_comma_break_excluded_type{$type_m} ) {
+                    ##my $token_m = $tokens_to_go[$ibreak_m];
+                    if ( $is_uncontained_comma_break_included_type{$type_m} ) {
                         $self->set_forced_breakpoint($ibreak);
                     }
                 }
@@ -15873,17 +18417,18 @@ sub set_continuation_breaks {
         return $is_sort_map_grep{ $container_type[$dd] };
     }
 
-    sub scan_list {
+    sub break_lists {
 
         my ( $self, $is_long_line ) = @_;
 
-        # This routine is responsible for setting line breaks for all lists,
-        # so that hierarchical structure can be displayed and so that list
-        # items can be vertically aligned.  The output of this routine is
-        # stored in the array @forced_breakpoint_to_go, which is used to set
-        # final breakpoints.
+        #----------------------------------------------------------------------
+        # This routine is called once per batch, if the batch is a list, to set
+        # line breaks so that hierarchical structure can be displayed and so
+        # that list items can be vertically aligned.  The output of this
+        # routine is stored in the array @forced_breakpoint_to_go, which is
+        # used by sub 'break_long_lines' to set final breakpoints.
+        #----------------------------------------------------------------------
 
-        # It is called once per batch if the batch is a list.
         my $rLL                  = $self->[_rLL_];
         my $ris_list_by_seqno    = $self->[_ris_list_by_seqno_];
         my $ris_broken_container = $self->[_ris_broken_container_];
@@ -15892,14 +18437,13 @@ sub set_continuation_breaks {
 
         $starting_depth = $nesting_depth_to_go[0];
 
-        $block_type                 = ' ';
-        $current_depth              = $starting_depth;
-        $i                          = -1;
-        $last_colon_sequence_number = -1;
-        $last_nonblank_token        = ';';
-        $last_nonblank_type         = ';';
-        $last_nonblank_block_type   = ' ';
-        $last_old_breakpoint_count  = 0;
+        $block_type                = ' ';
+        $current_depth             = $starting_depth;
+        $i                         = -1;
+        $last_nonblank_token       = ';';
+        $last_nonblank_type        = ';';
+        $last_nonblank_block_type  = ' ';
+        $last_old_breakpoint_count = 0;
         $minimum_depth = $current_depth + 1;    # forces update in check below
         $old_breakpoint_count      = 0;
         $starting_breakpoint_count = get_forced_breakpoint_count();
@@ -15910,6 +18454,7 @@ sub set_continuation_breaks {
         my $total_depth_variation = 0;
         my $i_old_assignment_break;
         my $depth_last = $starting_depth;
+        my $comma_follows_last_closing_token;
 
         check_for_new_minimum_depth($current_depth);
 
@@ -15918,8 +18463,11 @@ sub set_continuation_breaks {
         my $saw_good_breakpoint;
         my $i_line_end   = -1;
         my $i_line_start = -1;
+        my $i_last_colon = -1;
 
-        # loop over all tokens in this batch
+        #----------------------------------------
+        # Main loop over all tokens in this batch
+        #----------------------------------------
         while ( ++$i <= $max_index_to_go ) {
             if ( $type ne 'b' ) {
                 $i_last_nonblank_token    = $i - 1;
@@ -16014,10 +18562,11 @@ sub set_continuation_breaks {
             # formatting.
             if ( $type eq '#' ) {
                 if ( $i != $max_index_to_go ) {
-                    warning(
-"Non-fatal program bug: backup logic required to break after a comment\n"
-                    );
-                    report_definite_bug();
+                    if (DEVEL_MODE) {
+                        Fault(<<EOM);
+Non-fatal program bug: backup logic required to break after a comment
+EOM
+                    }
                     $nobreak_to_go[$i] = 0;
                     $self->set_forced_breakpoint($i);
                 } ## end if ( $i != $max_index_to_go)
@@ -16033,8 +18582,7 @@ sub set_continuation_breaks {
                 && $i > 0
 
                 # if one of these keywords:
-                #   /^(if|unless|while|until|for)$/
-                && $is_if_unless_while_until_for{$token}
+                && $is_if_unless_while_until_for_foreach{$token}
 
                 # but do not break at something like '1 while'
                 && ( $last_nonblank_type ne 'n' || $i > 2 )
@@ -16116,17 +18664,20 @@ sub set_continuation_breaks {
                 # handle any postponed closing breakpoints
                 if ( $is_closing_sequence_token{$token} ) {
                     if ( $type eq ':' ) {
-                        $last_colon_sequence_number = $type_sequence;
+                        $i_last_colon = $i;
 
                         # retain break at a ':' line break
-                        if ( ( $i == $i_line_start || $i == $i_line_end )
-                            && $rOpts_break_at_old_ternary_breakpoints )
+                        if (   ( $i == $i_line_start || $i == $i_line_end )
+                            && $rOpts_break_at_old_ternary_breakpoints
+                            && $levels_to_go[$i] < $list_stress_level )
                         {
 
                             $self->set_forced_breakpoint($i);
 
-                            # break at previous '='
-                            if ( $i_equals[$depth] > 0 ) {
+                            # Break at a previous '=', but only if it is before
+                            # the mating '?'. Mate_index test fixes b1287.
+                            my $ieq = $i_equals[$depth];
+                            if ( $ieq > 0 && $ieq < $mate_index_to_go[$i] ) {
                                 $self->set_forced_breakpoint(
                                     $i_equals[$depth] );
                                 $i_equals[$depth] = -1;
@@ -16135,7 +18686,9 @@ sub set_continuation_breaks {
                     } ## end if ( $type eq ':' )
                     if ( has_postponed_breakpoint($type_sequence) ) {
                         my $inc = ( $type eq ':' ) ? 0 : 1;
-                        $self->set_forced_breakpoint( $i - $inc );
+                        if ( $i >= $inc ) {
+                            $self->set_forced_breakpoint( $i - $inc );
+                        }
                     }
                 } ## end if ( $is_closing_sequence_token{$token} )
 
@@ -16152,22 +18705,46 @@ sub set_continuation_breaks {
                       )
                     {
 
+                        # don't break if # this has a side comment, and
                         # don't break at a '?' if preceded by ':' on
                         # this line of previous ?/: pair on this line.
                         # This is an attempt to preserve a chain of ?/:
-                        # expressions (elsif2.t).  And don't break if
-                        # this has a side comment.
-                        $self->set_forced_breakpoint($i)
-                          unless (
-                            $type_sequence == (
-                                $last_colon_sequence_number +
-                                  TYPE_SEQUENCE_INCREMENT
+                        # expressions (elsif2.t).
+                        if (
+                            (
+                                   $i_last_colon < 0
+                                || $parent_seqno_to_go[$i_last_colon] !=
+                                $parent_seqno_to_go[$i]
                             )
-                            || $tokens_to_go[$max_index_to_go] eq '#'
-                          );
+                            && $tokens_to_go[$max_index_to_go] ne '#'
+                          )
+                        {
+                            $self->set_forced_breakpoint($i);
+                        }
                         $self->set_closing_breakpoint($i);
                     } ## end if ( $i_colon <= 0  ||...)
                 } ## end elsif ( $token eq '?' )
+
+                elsif ( $is_opening_token{$token} ) {
+
+                    # do requeste -lp breaks at the OPENING token for BROKEN
+                    # blocks.  NOTE: this can be done for both -lp and -xlp,
+                    # but only -xlp can really take advantage of this.  So this
+                    # is currently restricted to -xlp to avoid excess changes to
+                    # existing -lp formatting.
+                    if (   $rOpts_extended_line_up_parentheses
+                        && $mate_index_to_go[$i] < 0 )
+                    {
+                        my $lp_object =
+                          $self->[_rlp_object_by_seqno_]->{$type_sequence};
+                        if ($lp_object) {
+                            my $K_begin_line = $lp_object->get_K_begin_line();
+                            my $i_begin_line = $K_begin_line - $K_to_go[0];
+                            $self->set_forced_lp_break( $i_begin_line, $i );
+                        }
+                    }
+                }
+
             } ## end if ($type_sequence)
 
 #print "LISTX sees: i=$i type=$type  tok=$token  block=$block_type depth=$depth\n";
@@ -16178,7 +18755,9 @@ sub set_continuation_breaks {
             # prepare for a new list when depth increases
             # token $i is a '(','{', or '['
             #------------------------------------------------------------
-            if ( $depth > $current_depth ) {
+            # hardened against bad input syntax: depth jump must be 1 and type
+            # must be opening..fixes c102
+            if ( $depth == $current_depth + 1 && $is_opening_type{$type} ) {
 
                 $type_sequence_stack[$depth] = $type_sequence;
                 $override_cab3[$depth] =
@@ -16240,7 +18819,7 @@ sub set_continuation_breaks {
                 # statements (like this one).  See similar coding in
                 # set_continuation breaks.  We have also catch it here for
                 # short line fragments which otherwise will not go through
-                # set_continuation_breaks.
+                # break_long_lines.
                 if (
                     $block_type
 
@@ -16249,7 +18828,7 @@ sub set_continuation_breaks {
                     && $mate_index_to_go[$i_last_nonblank_token] < 0
 
                     # and user wants brace to left
-                    && !$rOpts->{'opening-brace-always-on-right'}
+                    && !$rOpts_opening_brace_always_on_right
 
                     && ( $type eq '{' )     # should be true
                     && ( $token eq '{' )    # should be true
@@ -16265,23 +18844,28 @@ sub set_continuation_breaks {
             # finish off any old list when depth decreases
             # token $i is a ')','}', or ']'
             #------------------------------------------------------------
-            elsif ( $depth < $current_depth ) {
+            # hardened against bad input syntax: depth jump must be 1 and type
+            # must be closing .. fixes c102
+            elsif ( $depth == $current_depth - 1 && $is_closing_type{$type} ) {
 
                 check_for_new_minimum_depth($depth);
 
+                $comma_follows_last_closing_token =
+                  $next_nonblank_type eq ',' || $next_nonblank_type eq '=>';
+
                 # force all outer logical containers to break after we see on
                 # old breakpoint
                 $has_old_logical_breakpoints[$depth] ||=
                   $has_old_logical_breakpoints[$current_depth];
 
                 # Patch to break between ') {' if the paren list is broken.
-                # There is similar logic in set_continuation_breaks for
+                # There is similar logic in break_long_lines for
                 # non-broken lists.
                 if (   $token eq ')'
                     && $next_nonblank_block_type
                     && $interrupted_list[$current_depth]
                     && $next_nonblank_type eq '{'
-                    && !$rOpts->{'opening-brace-always-on-right'} )
+                    && !$rOpts_opening_brace_always_on_right )
                 {
                     $self->set_forced_breakpoint($i);
                 } ## end if ( $token eq ')' && ...
@@ -16294,6 +18878,11 @@ sub set_continuation_breaks {
 
                 my $i_opening = $opening_structure_index_stack[$current_depth];
                 my $saw_opening_structure = ( $i_opening >= 0 );
+                my $lp_object;
+                if ( $rOpts_line_up_parentheses && $saw_opening_structure ) {
+                    $lp_object = $self->[_rlp_object_by_seqno_]
+                      ->{ $type_sequence_to_go[$i_opening] };
+                }
 
                 # this term is long if we had to break at interior commas..
                 my $is_long_term = $bp_count > 0;
@@ -16317,6 +18906,32 @@ sub set_continuation_breaks {
                     $cab_flag = 5;
                 }
 
+                # Ignore old breakpoints when under stress.
+                # Fixes b1203 b1204 as well as b1197-b1200.
+                # But not if -lp: fixes b1264, b1265.  NOTE: rechecked with
+                # b1264 to see if this check is still required at all, and
+                # these still require a check, but at higher level beta+3
+                # instead of beta:  b1193 b780
+                if (   $saw_opening_structure
+                    && !$lp_object
+                    && $levels_to_go[$i_opening] >= $list_stress_level )
+                {
+                    $cab_flag = 2;
+
+                    # Do not break hash braces under stress (fixes b1238)
+                    $do_not_break_apart ||= $types_to_go[$i_opening] eq 'L';
+
+                   # This option fixes b1235, b1237, b1240 with old and new -lp,
+                   # but formatting is nicer with next option.
+                    ## $is_long_term ||=
+                    ##  $levels_to_go[$i_opening] > $stress_level_beta + 1;
+
+                    # This option fixes b1240 but not b1235, b1237 with new -lp,
+                    # but this gives better formatting than the previous option.
+                    $do_not_break_apart ||=
+                      $levels_to_go[$i_opening] > $stress_level_beta;
+                }
+
                 if (  !$is_long_term
                     && $saw_opening_structure
                     && $is_opening_token{ $tokens_to_go[$i_opening] }
@@ -16340,11 +18955,33 @@ sub set_continuation_breaks {
                     my $excess =
                       $self->excess_line_length( $i_opening_minus, $i );
 
-                    my $tol =
-                      $length_tol_boost
-                      && !$ris_list_by_seqno->{$type_sequence}
-                      ? $length_tol + $length_tol_boost
-                      : $length_tol;
+                    # Use standard spaces for indentation of lists in -lp mode
+                    # if it gives a longer line length. This helps to avoid an
+                    # instability due to forming and breaking one-line blocks.
+                    # This fixes case b1314.
+                    my $indentation = $leading_spaces_to_go[$i_opening_minus];
+                    if ( ref($indentation)
+                        && $ris_broken_container->{$type_sequence} )
+                    {
+                        my $lp_spaces = $indentation->get_spaces();
+                        my $std_spaces =
+                          $standard_spaces_to_go[$i_opening_minus];
+                        my $diff = $std_spaces - $lp_spaces;
+                        if ( $diff > 0 ) { $excess += $diff }
+                    }
+
+                    my $tol = $length_tol;
+
+                    # boost tol for an -lp container
+                    if (
+                           $lp_tol_boost
+                        && $lp_object
+                        && ( $rOpts_extended_continuation_indentation
+                            || !$ris_list_by_seqno->{$type_sequence} )
+                      )
+                    {
+                        $tol += $lp_tol_boost;
+                    }
 
                     # Patch to avoid blinking with -bbxi=2 and -cab=2
                     # in which variations in -ci cause unstable formatting
@@ -16523,8 +19160,7 @@ sub set_continuation_breaks {
 
                     # open up a long 'for' or 'foreach' container to allow
                     # leading term alignment unless -lp is used.
-                    $has_comma_breakpoints = 1
-                      unless $rOpts_line_up_parentheses;
+                    $has_comma_breakpoints = 1 unless ($lp_object);
                 } ## end if ( $is_long_term && ...)
 
                 if (
@@ -16559,63 +19195,24 @@ sub set_continuation_breaks {
                   )
                 {
 
-                    # For -lp option, we must put a breakpoint before
-                    # the token which has been identified as starting
-                    # this indentation level.  This is necessary for
-                    # proper alignment.
-                    if ( $rOpts_line_up_parentheses && $saw_opening_structure )
-                    {
-                        my $item = $leading_spaces_to_go[ $i_opening + 1 ];
-                        if (   $i_opening + 1 < $max_index_to_go
-                            && $types_to_go[ $i_opening + 1 ] eq 'b' )
-                        {
-                            $item = $leading_spaces_to_go[ $i_opening + 2 ];
-                        }
-                        if ( defined($item) ) {
-                            my $i_start_2;
-                            my $K_start_2 = $item->get_starting_index_K();
-                            if ( defined($K_start_2) ) {
-                                $i_start_2 = $K_start_2 - $K_to_go[0];
-                            }
-                            if (
-                                defined($i_start_2)
-
-                                # we are breaking after an opening brace, paren,
-                                # so don't break before it too
-                                && $i_start_2 ne $i_opening
-                                && $i_start_2 >= 0
-                                && $i_start_2 <= $max_index_to_go
-                              )
-                            {
-
-                                # Only break for breakpoints at the same
-                                # indentation level as the opening paren
-                                my $test1 = $nesting_depth_to_go[$i_opening];
-                                my $test2 = $nesting_depth_to_go[$i_start_2];
-                                if ( $test2 == $test1 ) {
-
-                                    # Back up at a blank (fixes case b932)
-                                    my $ibr = $i_start_2 - 1;
-                                    if (   $ibr > 0
-                                        && $types_to_go[$ibr] eq 'b' )
-                                    {
-                                        $ibr--;
-                                    }
-
-                                    $self->set_forced_breakpoint($ibr);
-
-                                }
-                            } ## end if ( defined($i_start_2...))
-                        } ## end if ( defined($item) )
-                    } ## end if ( $rOpts_line_up_parentheses...)
+                    # do special -lp breaks at the CLOSING token for INTACT
+                    # blocks (because we might not do them if the block does
+                    # not break open)
+                    if ($lp_object) {
+                        my $K_begin_line = $lp_object->get_K_begin_line();
+                        my $i_begin_line = $K_begin_line - $K_to_go[0];
+                        $self->set_forced_lp_break( $i_begin_line, $i_opening );
+                    }
 
                     # break after opening structure.
                     # note: break before closing structure will be automatic
                     if ( $minimum_depth <= $current_depth ) {
 
-                        $self->set_forced_breakpoint($i_opening)
-                          unless ( $do_not_break_apart
-                            || is_unbreakable_container($current_depth) );
+                        if ( $i_opening >= 0 ) {
+                            $self->set_forced_breakpoint($i_opening)
+                              unless ( $do_not_break_apart
+                                || is_unbreakable_container($current_depth) );
+                        }
 
                         # break at ',' of lower depth level before opening token
                         if ( $last_comma_index[$depth] ) {
@@ -16688,7 +19285,7 @@ sub set_continuation_breaks {
                             $self->set_forced_breakpoint($icomma);
                         }
                     }
-                }    # end logic to open up a container
+                } ## end logic to open up a container
 
                 # Break open a logical container open if it was already open
                 elsif ($is_simple_logical_expression
@@ -16812,8 +19409,13 @@ sub set_continuation_breaks {
                 next;
             } ## end if ( $want_comma_break...)
 
-            # break after all commas above starting depth
-            if ( $depth < $starting_depth && !$dont_align[$depth] ) {
+            # Break after all commas above starting depth...
+            # But only if the last closing token was followed by a comma,
+            #   to avoid breaking a list operator (issue c119)
+            if (   $depth < $starting_depth
+                && $comma_follows_last_closing_token
+                && !$dont_align[$depth] )
+            {
                 $self->set_forced_breakpoint($i)
                   unless ( $next_nonblank_type eq '#' );
                 next;
@@ -16858,16 +19460,18 @@ sub set_continuation_breaks {
 
             # break open container...
             my $i_opening = $opening_structure_index_stack[$dd];
-            $self->set_forced_breakpoint($i_opening)
-              unless (
-                is_unbreakable_container($dd)
+            if ( defined($i_opening) && $i_opening >= 0 ) {
+                $self->set_forced_breakpoint($i_opening)
+                  unless (
+                    is_unbreakable_container($dd)
 
-                # Avoid a break which would place an isolated ' or "
-                # on a line
-                || (   $type eq 'Q'
-                    && $i_opening >= $max_index_to_go - 2
-                    && ( $token eq "'" || $token eq '"' ) )
-              );
+                    # Avoid a break which would place an isolated ' or "
+                    # on a line
+                    || (   $type eq 'Q'
+                        && $i_opening >= $max_index_to_go - 2
+                        && ( $token eq "'" || $token eq '"' ) )
+                  );
+            }
         } ## end for ( my $dd = $current_depth...)
 
         # Return a flag indicating if the input file had some good breakpoints.
@@ -16893,18 +19497,26 @@ sub set_continuation_breaks {
         } ## end elsif ( $i_old_assignment_break...)
 
         return $saw_good_breakpoint;
-    } ## end sub scan_list
-} ## end closure scan_list
+    } ## end sub break_lists
+} ## end closure break_lists
 
 my %is_kwiZ;
+my %is_key_type;
 
 BEGIN {
 
     # Added 'w' to fix b1172
-    my @q = qw(k w i Z);
+    my @q = qw(k w i Z ->);
     @is_kwiZ{@q} = (1) x scalar(@q);
+
+    # added = for b1211
+    @q = qw<( [ { L R } ] ) = b>;
+    push @q, ',';
+    @is_key_type{@q} = (1) x scalar(@q);
 }
 
+use constant DEBUG_FIND_START => 0;
+
 sub find_token_starting_list {
 
     # When testing to see if a block will fit on one line, some
@@ -16930,7 +19542,14 @@ sub find_token_starting_list {
         # a previous comma is a good break point
         # $i_opening_minus = $i_opening_paren;
     }
-    elsif ( $tokens_to_go[$i_opening_paren] eq '(' ) {
+
+    elsif (
+        $tokens_to_go[$i_opening_paren] eq '('
+
+        # non-parens added here to fix case b1186
+        || $is_kwiZ{$type_prev_nb}
+      )
+    {
         $i_opening_minus = $im1;
 
         # Walk back to improve length estimate...
@@ -16939,17 +19558,25 @@ sub find_token_starting_list {
         # to the flag --space-function-paren, and similar.
         # previous loop: for ( my $j = $im1 ; $j >= 0 ; $j-- ) {
         for ( my $j = $iprev_nb ; $j >= 0 ; $j-- ) {
-            last if ( $types_to_go[$j] =~ /^[\(\[\{L\}\]\)Rb,]$/ );
+            ##last if ( $types_to_go[$j] =~ /^[\(\[\{L\}\]\)Rb,]$/ );
+            ##last if ( $is_key_type{ $types_to_go[$j] } );
+            if ( $is_key_type{ $types_to_go[$j] } ) {
+
+                # fix for b1211
+                if ( $types_to_go[$j] eq '=' ) { $i_opening_minus = $j }
+                last;
+            }
             $i_opening_minus = $j;
         }
         if ( $types_to_go[$i_opening_minus] eq 'b' ) { $i_opening_minus++ }
     }
 
-    # Handle non-parens
-    elsif ( $is_kwiZ{$type_prev_nb} ) { $i_opening_minus = $iprev_nb }
-
   RETURN:
 
+    DEBUG_FIND_START && print <<EOM;
+FIND_START: i=$i_opening_paren tok=$tokens_to_go[$i_opening_paren] => im=$i_opening_minus tok=$tokens_to_go[$i_opening_minus]
+EOM
+
     return $i_opening_minus;
 }
 
@@ -16998,6 +19625,7 @@ sub find_token_starting_list {
             $i_last_comma = $rcomma_index->[ --$item_count - 1 ];
             return if ( $item_count < 1 );
         }
+        my $is_lp_formatting = ref( $leading_spaces_to_go[$i_first_comma] );
 
         #---------------------------------------------------------------
         # find lengths of all items in the list to calculate page layout
@@ -17175,10 +19803,20 @@ sub find_token_starting_list {
         # Return if this will fit on one line
         #-------------------------------------------------------------------
 
+        # The -bbxi=2 parameters can add an extra hidden level of indentation;
+        # this needs a tolerance to avoid instability.  Fixes b1259, 1260.
+        my $tol = 0;
+        if (   $break_before_container_types{$opening_token}
+            && $container_indentation_options{$opening_token}
+            && $container_indentation_options{$opening_token} == 2 )
+        {
+            $tol = $rOpts_indent_columns;
+        }
+
         my $i_opening_minus = $self->find_token_starting_list($i_opening_paren);
         return
           unless $self->excess_line_length( $i_opening_minus, $i_closing_paren )
-          > 0;
+          + $tol > 0;
 
         #-------------------------------------------------------------------
         # Now we know that this block spans multiple lines; we have to set
@@ -17196,7 +19834,7 @@ sub find_token_starting_list {
         # items aligned.  This is necessary if any of the list terms
         # exceeds the available space after the '('.
         my $need_lp_break_open = $must_break_open;
-        if ( $rOpts_line_up_parentheses && !$must_break_open ) {
+        if ( $is_lp_formatting && !$must_break_open ) {
             my $columns_if_unbroken =
               $maximum_line_length_at_level[ $levels_to_go[$i_opening_minus] ]
               - total_line_length( $i_opening_minus, $i_opening_paren );
@@ -17286,6 +19924,37 @@ sub find_token_starting_list {
         # Number of free columns across the page width for laying out tables
         my $columns = table_columns_available($i_first_comma);
 
+        # Patch for b1210 and b1216-b1218 when -vmll is set.  If we are unable
+        # to break after an opening paren, then the maximum line length for the
+        # first line could be less than the later lines.  So we need to reduce
+        # the line length.  Normally, we will get a break after an opening
+        # paren, but in some cases we might not.
+        if (   $rOpts_variable_maximum_line_length
+            && $tokens_to_go[$i_opening_paren] eq '('
+            && @i_term_begin )
+          ##&& !$old_breakpoint_to_go[$i_opening_paren] )  ## in b1210 patch
+        {
+            my $ib   = $i_term_begin[0];
+            my $type = $types_to_go[$ib];
+
+            # So far, the only known instance of this problem is when
+            # a bareword follows an opening paren with -vmll
+            if ( $type eq 'w' ) {
+
+                # If a line starts with paren+space+terms, then its max length
+                # could be up to ci+2-i spaces less than if the term went out
+                # on a line after the paren.  So..
+                my $tol = max( 0,
+                    2 + $rOpts_continuation_indentation -
+                      $rOpts_indent_columns );
+                $columns = max( 0, $columns - $tol );
+
+                ## Here is the original b1210 fix, but it failed on b1216-b1218
+                ##my $columns2 = table_columns_available($i_opening_paren);
+                ##$columns = min( $columns, $columns2 );
+            }
+        }
+
         # Estimated maximum number of fields which fit this space
         # This will be our first guess
         my $number_of_fields_max =
@@ -17311,7 +19980,7 @@ sub find_token_starting_list {
         # undo some indentation
         # ----------------------------------------------------------------------
         if (
-            $rOpts_line_up_parentheses
+            $is_lp_formatting
             && (
                 $number_of_fields == 0
                 || (   $number_of_fields == 1
@@ -17513,22 +20182,53 @@ sub find_token_starting_list {
           : ( $packed_lines == 2 ) ? 0.4
           :                          0.7;
 
+        my $two_line_word_wrap_ok;
+        if ( $opening_token eq '(' ) {
+
+            # default is to allow wrapping of short paren lists
+            $two_line_word_wrap_ok = 1;
+
+            # but turn off word wrap where requested
+            if ($rOpts_break_open_paren_list) {
+
+                # This parameter is a one-character flag, as follows:
+                #  '0' matches no parens  -> break open NOT OK -> word wrap OK
+                #  '1' matches all parens -> break open OK -> word wrap NOT OK
+                #  Other values are the same as used by the weld-exclusion-list
+                my $flag = $rOpts_break_open_paren_list;
+                if (   $flag eq '*'
+                    || $flag eq '1' )
+                {
+                    $two_line_word_wrap_ok = 0;
+                }
+                elsif ( $flag eq '0' ) {
+                    $two_line_word_wrap_ok = 1;
+                }
+                else {
+                    my $KK = $K_to_go[$i_opening_paren];
+                    $two_line_word_wrap_ok =
+                      !$self->match_paren_flag( $KK, $flag );
+                }
+            }
+        }
+
         # Begin check for shortcut methods, which avoid treating a list
         # as a table for relatively small parenthesized lists.  These
         # are usually easier to read if not formatted as tables.
         if (
-            $packed_lines <= 2          # probably can fit in 2 lines
-            && $item_count < 9          # doesn't have too many items
-            && $opening_is_in_block     # not a sub-container
-            && $opening_token eq '('    # is paren list
+            $packed_lines <= 2           # probably can fit in 2 lines
+            && $item_count < 9           # doesn't have too many items
+            && $opening_is_in_block      # not a sub-container
+            && $two_line_word_wrap_ok    # ok to wrap this paren list
+            ##&& $opening_token eq '('    # is paren list
           )
         {
 
             # Shortcut method 1: for -lp and just one comma:
             # This is a no-brainer, just break at the comma.
             if (
-                $rOpts_line_up_parentheses    # -lp
-                && $item_count == 2           # two items, one comma
+                $is_lp_formatting      # -lp
+                && $item_count == 2    # two items, one comma
                 && !$must_break_open
               )
             {
@@ -17562,15 +20262,14 @@ sub find_token_starting_list {
                     if ( $break_count <= 1 ) {
                         ${$rdo_not_break_apart} = 1;
                     }
-                    elsif ( $rOpts_line_up_parentheses && !$need_lp_break_open )
-                    {
+                    elsif ( $is_lp_formatting && !$need_lp_break_open ) {
                         ${$rdo_not_break_apart} = 1;
                     }
                 }
                 return;
             }
 
-        }    # end shortcut methods
+        } ## end shortcut methods
 
         # debug stuff
         DEBUG_SPARSE && do {
@@ -17619,7 +20318,7 @@ sub find_token_starting_list {
         # structure.
         my $must_break_open_container = $must_break_open
           || ( $too_long
-            && ( $in_hierarchical_list || $opening_token ne '(' ) );
+            && ( $in_hierarchical_list || !$two_line_word_wrap_ok ) );
 
 #print "LISTX: next=$next_nonblank_type  avail cols=$columns packed=$packed_columns must format = $must_break_open_container too-long=$too_long  opening=$opening_token list_type=$list_type formatted_lines=$formatted_lines  packed=$packed_lines max_sparsity= $max_allowed_sparsity sparsity=$sparsity \n";
 
@@ -17658,8 +20357,7 @@ sub find_token_starting_list {
                     if ( $break_count <= 1 ) {
                         ${$rdo_not_break_apart} = 1;
                     }
-                    elsif ( $rOpts_line_up_parentheses && !$need_lp_break_open )
-                    {
+                    elsif ( $is_lp_formatting && !$need_lp_break_open ) {
                         ${$rdo_not_break_apart} = 1;
                     }
                 }
@@ -18061,566 +20759,830 @@ sub get_available_spaces_to_go {
     return ref($item) ? $item->get_available_spaces() : 0;
 }
 
-{    ## begin closure set_leading_whitespace (for -lp indentation)
+{    ## begin closure set_lp_indentation
 
-    # These routines are called batch-by-batch to handle the -lp indentation
-    # option.  The coding is rather complex, but is only for -lp.
+    use constant DEBUG_LP => 0;
 
-    my $gnu_position_predictor;
-    my $gnu_sequence_number;
-    my $line_start_index_to_go;
-    my $max_gnu_item_index;
-    my $max_gnu_stack_index;
-    my %gnu_arrow_count;
-    my %gnu_comma_count;
-    my %last_gnu_equals;
-    my @gnu_item_list;
-    my @gnu_stack;
+    # Stack of -lp index objects which survives between batches.
+    my $rLP;
+    my $max_lp_stack;
 
-    sub initialize_gnu_vars {
+    # The predicted position of the next opening container which may start
+    # an -lp indentation level.  This survives between batches.
+    my $lp_position_predictor;
 
-        # initialize gnu variables for a new file;
-        # must be called once at the start of a new file.
+    # A level at which the lp format becomes too highly stressed to continue
+    my $lp_cutoff_level;
 
-        # initialize the leading whitespace stack to negative levels
-        # so that we can never run off the end of the stack
-        $gnu_position_predictor =
-          0;    # where the current token is predicted to be
-        $max_gnu_stack_index = 0;
-        $max_gnu_item_index  = -1;
-        $gnu_stack[0]        = new_lp_indentation_item( 0, -1, -1, 0, 0 );
-        @gnu_item_list       = ();
-        return;
-    }
+    BEGIN {
 
-    sub initialize_gnu_batch_vars {
+        # Index names for the -lp stack variables.
+        # Do not combine with other BEGIN blocks (c101).
 
-        # initialize gnu variables for a new batch;
-        # must be called before each new batch
-        $gnu_sequence_number++;    # increment output batch counter
-        %last_gnu_equals        = ();
-        %gnu_comma_count        = ();
-        %gnu_arrow_count        = ();
-        $line_start_index_to_go = 0;
-        $max_gnu_item_index     = UNDEFINED_INDEX;
-        return;
+        my $i = 0;
+        use constant {
+            _lp_ci_level_        => $i++,
+            _lp_level_           => $i++,
+            _lp_object_          => $i++,
+            _lp_container_seqno_ => $i++,
+            _lp_space_count_     => $i++,
+        };
     }
 
-    sub new_lp_indentation_item {
+    sub initialize_lp_vars {
 
-        # this is an interface to the IndentationItem class
-        my ( $spaces, $level, $ci_level, $available_spaces, $align_paren ) = @_;
-
-        # A negative level implies not to store the item in the item_list
-        my $index = 0;
-        if ( $level >= 0 ) { $index = ++$max_gnu_item_index; }
+        # initialize gnu variables for a new file;
+        # must be called once at the start of a new file.
 
-        my $starting_index_K = 0;
-        if (   defined($line_start_index_to_go)
-            && $line_start_index_to_go >= 0
-            && $line_start_index_to_go <= $max_index_to_go )
-        {
-            $starting_index_K = $K_to_go[$line_start_index_to_go];
-        }
-
-        my $item = Perl::Tidy::IndentationItem->new(
-            spaces              => $spaces,
-            level               => $level,
-            ci_level            => $ci_level,
-            available_spaces    => $available_spaces,
-            index               => $index,
-            gnu_sequence_number => $gnu_sequence_number,
-            align_paren         => $align_paren,
-            stack_depth         => $max_gnu_stack_index,
-            starting_index_K    => $starting_index_K,
-        );
+        $lp_position_predictor = 0;
+        $max_lp_stack          = 0;
+        $lp_cutoff_level = min( $stress_level_alpha, $stress_level_beta + 2 );
 
-        if ( $level >= 0 ) {
-            $gnu_item_list[$max_gnu_item_index] = $item;
+        # we can turn off -lp if all levels will be at or above the cutoff
+        if ( $lp_cutoff_level <= 1 ) {
+            $rOpts_line_up_parentheses          = 0;
+            $rOpts_extended_line_up_parentheses = 0;
         }
 
-        return $item;
+        $rLP = [];
+
+        # initialize the leading whitespace stack to negative levels
+        # so that we can never run off the end of the stack
+        $rLP->[$max_lp_stack]->[_lp_ci_level_]        = -1;
+        $rLP->[$max_lp_stack]->[_lp_level_]           = -1;
+        $rLP->[$max_lp_stack]->[_lp_object_]          = undef;
+        $rLP->[$max_lp_stack]->[_lp_container_seqno_] = SEQ_ROOT;
+        $rLP->[$max_lp_stack]->[_lp_space_count_]     = 0;
+
+        return;
     }
 
-    sub set_leading_whitespace {
+    # hashes for efficient testing
+    my %hash_test1;
+    my %hash_test2;
+    my %hash_test3;
 
-        # This routine defines leading whitespace for the case of -lp formatting
-        # given: the level and continuation_level of a token,
-        # define: space count of leading string which would apply if it
-        # were the first token of a new line.
+    BEGIN {
+        my @q = qw< } ) ] >;
+        @hash_test1{@q} = (1) x scalar(@q);
+        @q = qw(: ? f);
+        push @q, ',';
+        @hash_test2{@q} = (1) x scalar(@q);
+        @q              = qw( . || && );
+        @hash_test3{@q} = (1) x scalar(@q);
+    }
 
-        my ( $self, $Kj, $K_last_nonblank, $K_last_last_nonblank,
-            $level_abs, $ci_level, $in_continued_quote )
-          = @_;
+    sub set_lp_indentation {
+
+        #------------------------------------------------------------------
+        # Define the leading whitespace for all tokens in the current batch
+        # when the -lp formatting is selected.
+        #------------------------------------------------------------------
+
+        my ($self) = @_;
 
         return unless ($rOpts_line_up_parentheses);
         return unless ( defined($max_index_to_go) && $max_index_to_go >= 0 );
 
+        # List of -lp indentation objects created in this batch
+        my $rlp_object_list    = [];
+        my $max_lp_object_list = UNDEFINED_INDEX;
+
+        my %last_lp_equals;
+        my %lp_comma_count;
+        my %lp_arrow_count;
+        my $ii_begin_line = 0;
+
+        my $rLL                       = $self->[_rLL_];
+        my $Klimit                    = $self->[_Klimit_];
         my $rbreak_container          = $self->[_rbreak_container_];
         my $rshort_nested             = $self->[_rshort_nested_];
         my $ris_excluded_lp_container = $self->[_ris_excluded_lp_container_];
-        my $rLL                       = $self->[_rLL_];
+        my $rblock_type_of_seqno      = $self->[_rblock_type_of_seqno_];
+        my $starting_in_quote   = $self->[_this_batch_]->[_starting_in_quote_];
+        my $K_opening_container = $self->[_K_opening_container_];    ##TESTING
+        my $K_closing_container = $self->[_K_closing_container_];
+        my $rlp_object_by_seqno = $self->[_rlp_object_by_seqno_];
+        my $radjusted_levels    = $self->[_radjusted_levels_];
         my $rbreak_before_container_by_seqno =
           $self->[_rbreak_before_container_by_seqno_];
+        my $rcollapsed_length_by_seqno = $self->[_rcollapsed_length_by_seqno_];
+
+        my $nws  = @{$radjusted_levels};
+        my $imin = 0;
+
+        # The 'starting_in_quote' flag means that the first token is the first
+        # token of a line and it is also the continuation of some kind of
+        # multi-line quote or pattern.  It must have no added leading
+        # whitespace, so we can skip it.
+        if ($starting_in_quote) {
+            $imin += 1;
+        }
 
-        # find needed previous nonblank tokens
-        my $last_nonblank_token      = '';
-        my $last_nonblank_type       = '';
-        my $last_nonblank_block_type = '';
+        my $K_last_nonblank;
+        my $Kpnb = $K_to_go[0] - 1;
+        if ( $Kpnb > 0 && $rLL->[$Kpnb]->[_TYPE_] eq 'b' ) {
+            $Kpnb -= 1;
+        }
+        if ( $Kpnb >= 0 && $rLL->[$Kpnb]->[_TYPE_] ne 'b' ) {
+            $K_last_nonblank = $Kpnb;
+        }
 
-        # and previous nonblank tokens, just in this batch:
-        my $last_nonblank_token_in_batch     = '';
-        my $last_nonblank_type_in_batch      = '';
-        my $last_last_nonblank_type_in_batch = '';
+        my $last_nonblank_token     = '';
+        my $last_nonblank_type      = '';
+        my $last_last_nonblank_type = '';
 
         if ( defined($K_last_nonblank) ) {
             $last_nonblank_token = $rLL->[$K_last_nonblank]->[_TOKEN_];
             $last_nonblank_type  = $rLL->[$K_last_nonblank]->[_TYPE_];
-            $last_nonblank_block_type =
-              $rLL->[$K_last_nonblank]->[_BLOCK_TYPE_];
-
-            if ( $K_last_nonblank >= $K_to_go[0] ) {
-                $last_nonblank_token_in_batch = $last_nonblank_token;
-                $last_nonblank_type_in_batch  = $last_nonblank_type;
-                if ( defined($K_last_last_nonblank)
-                    && $K_last_last_nonblank > $K_to_go[0] )
-                {
-                    $last_last_nonblank_type_in_batch =
-                      $rLL->[$K_last_last_nonblank]->[_TYPE_];
-                }
-            }
         }
 
-        ################################################################
+        my ( $space_count, $current_level, $current_ci_level, $in_lp_mode );
+        my $stack_changed = 1;
 
-        # Adjust levels if necessary to recycle whitespace:
-        my $level            = $level_abs;
-        my $radjusted_levels = $self->[_radjusted_levels_];
-        my $nK               = @{$rLL};
-        my $nws              = @{$radjusted_levels};
-        if ( defined($radjusted_levels) && @{$radjusted_levels} == @{$rLL} ) {
-            $level = $radjusted_levels->[$Kj];
-            if ( $level < 0 ) { $level = 0 }    # note: this should not happen
-        }
+        #-----------------------------------
+        # Loop over all tokens in this batch
+        #-----------------------------------
+        foreach my $ii ( $imin .. $max_index_to_go ) {
 
-        # The continued_quote flag means that this is the first token of a
-        # line, and it is the continuation of some kind of multi-line quote
-        # or pattern.  It requires special treatment because it must have no
-        # added leading whitespace. So we create a special indentation item
-        # which is not in the stack.
-        if ($in_continued_quote) {
-            my $space_count     = 0;
-            my $available_space = 0;
-            $level = -1;    # flag to prevent storing in item_list
-            $leading_spaces_to_go[$max_index_to_go] =
-              $reduced_spaces_to_go[$max_index_to_go] =
-              new_lp_indentation_item( $space_count, $level, $ci_level,
-                $available_space, 0 );
-            return;
-        }
+            my $KK          = $K_to_go[$ii];
+            my $type        = $types_to_go[$ii];
+            my $token       = $tokens_to_go[$ii];
+            my $level       = $levels_to_go[$ii];
+            my $ci_level    = $ci_levels_to_go[$ii];
+            my $total_depth = $nesting_depth_to_go[$ii];
+
+            #--------------------------------------------------
+            # Adjust levels if necessary to recycle whitespace:
+            #--------------------------------------------------
+            if ( defined($radjusted_levels) && @{$radjusted_levels} == $Klimit )
+            {
+                $level = $radjusted_levels->[$KK];
+                if ( $level < 0 ) { $level = 0 }  # note: this should not happen
+            }
 
-        # get the top state from the stack
-        my $space_count      = $gnu_stack[$max_gnu_stack_index]->get_spaces();
-        my $current_level    = $gnu_stack[$max_gnu_stack_index]->get_level();
-        my $current_ci_level = $gnu_stack[$max_gnu_stack_index]->get_ci_level();
+            # get the top state from the stack if it has changed
+            if ($stack_changed) {
+                my $rLP_top   = $rLP->[$max_lp_stack];
+                my $lp_object = $rLP_top->[_lp_object_];
+                if ($lp_object) {
+                    ( $space_count, $current_level, $current_ci_level ) =
+                      @{ $lp_object->get_spaces_level_ci() };
+                }
+                else {
+                    $current_ci_level = $rLP_top->[_lp_ci_level_];
+                    $current_level    = $rLP_top->[_lp_level_];
+                    $space_count      = $rLP_top->[_lp_space_count_];
+                }
+                $stack_changed = 0;
+            }
 
-        my $type        = $types_to_go[$max_index_to_go];
-        my $token       = $tokens_to_go[$max_index_to_go];
-        my $total_depth = $nesting_depth_to_go[$max_index_to_go];
+            #------------------------------
+            # update the position predictor
+            #------------------------------
+            if ( $type eq '{' || $type eq '(' ) {
 
-        if ( $type eq '{' || $type eq '(' ) {
+                $lp_comma_count{ $total_depth + 1 } = 0;
+                $lp_arrow_count{ $total_depth + 1 } = 0;
 
-            $gnu_comma_count{ $total_depth + 1 } = 0;
-            $gnu_arrow_count{ $total_depth + 1 } = 0;
+                # If we come to an opening token after an '=' token of some
+                # type, see if it would be helpful to 'break' after the '=' to
+                # save space
+                my $last_equals = $last_lp_equals{$total_depth};
+                if ( $last_equals && $last_equals > $ii_begin_line ) {
 
-            # If we come to an opening token after an '=' token of some type,
-            # see if it would be helpful to 'break' after the '=' to save space
-            my $last_equals = $last_gnu_equals{$total_depth};
-            if ( $last_equals && $last_equals > $line_start_index_to_go ) {
+                    my $seqno = $type_sequence_to_go[$ii];
 
-                my $seqno = $type_sequence_to_go[$max_index_to_go];
+                    # find the position if we break at the '='
+                    my $i_test = $last_equals;
 
-                # find the position if we break at the '='
-                my $i_test = $last_equals;
-                if ( $types_to_go[ $i_test + 1 ] eq 'b' ) { $i_test++ }
+                    # Fix for issue b1229, check for break before
+                    if ( $want_break_before{ $types_to_go[$i_test] } ) {
+                        if ( $i_test > 0 ) { $i_test-- }
+                    }
+                    elsif ( $types_to_go[ $i_test + 1 ] eq 'b' ) { $i_test++ }
 
-                # TESTING
-                ##my $too_close = ($i_test==$max_index_to_go-1);
+                    # TESTING
+                    ##my $too_close = ($i_test==$ii-1);
 
-                my $test_position =
-                  total_line_length( $i_test, $max_index_to_go );
-                my $mll =
-                  $maximum_line_length_at_level[ $levels_to_go[$i_test] ];
+                    my $test_position = total_line_length( $i_test, $ii );
+                    my $mll =
+                      $maximum_line_length_at_level[ $levels_to_go[$i_test] ];
 
-                my $bbc_flag = $break_before_container_types{$token};
+                    #------------------------------------------------------
+                    # Break if structure will reach the maximum line length
+                    #------------------------------------------------------
 
-                if (
+                    # Historically, -lp just used one-half line length here
+                    my $len_increase = $rOpts_maximum_line_length / 2;
+
+                    # For -xlp, we can also use the pre-computed lengths
+                    my $min_len = $rcollapsed_length_by_seqno->{$seqno};
+                    if ( $min_len && $min_len > $len_increase ) {
+                        $len_increase = $min_len;
+                    }
 
-                    # the equals is not just before an open paren (testing)
-                    ##!$too_close &&
+                    if (
 
-                    # if we are beyond the midpoint
-                    $gnu_position_predictor >
-                    $mll - $rOpts_maximum_line_length / 2
+                        # the equals is not just before an open paren (testing)
+                        ##!$too_close &&
 
-                    # if a -bbx flag WANTS a break before this opening token
-                    || ( $seqno && $rbreak_before_container_by_seqno->{$seqno} )
+                        # if we might exceed the maximum line length
+                        $lp_position_predictor + $len_increase > $mll
 
-                    # or if we MIGHT want a break (fixes case b826 b909 b989)
-                    || ( $bbc_flag && $bbc_flag >= 2 )
+                        # if a -bbx flag WANTS a break before this opening token
+                        || (   $seqno
+                            && $rbreak_before_container_by_seqno->{$seqno} )
 
-                    # or we are beyond the 1/4 point and there was an old
-                    # break at an assignment (not '=>') [fix for b1035]
-                    || (
-                        $gnu_position_predictor >
-                        $mll - $rOpts_maximum_line_length * 3 / 4
-                        && $types_to_go[$last_equals] ne '=>'
-                        && (
-                            $old_breakpoint_to_go[$last_equals]
-                            || (   $last_equals > 0
-                                && $old_breakpoint_to_go[ $last_equals - 1 ] )
-                            || (   $last_equals > 1
-                                && $types_to_go[ $last_equals - 1 ] eq 'b'
-                                && $old_breakpoint_to_go[ $last_equals - 2 ] )
+                        # or we are beyond the 1/4 point and there was an old
+                        # break at an assignment (not '=>') [fix for b1035]
+                        || (
+                            $lp_position_predictor >
+                            $mll - $rOpts_maximum_line_length * 3 / 4
+                            && $types_to_go[$last_equals] ne '=>'
+                            && (
+                                $old_breakpoint_to_go[$last_equals]
+                                || (   $last_equals > 0
+                                    && $old_breakpoint_to_go[ $last_equals - 1 ]
+                                )
+                                || (   $last_equals > 1
+                                    && $types_to_go[ $last_equals - 1 ] eq 'b'
+                                    && $old_breakpoint_to_go[ $last_equals - 2 ]
+                                )
+                            )
                         )
-                    )
-                  )
-                {
+                      )
+                    {
 
-                    # then make the switch -- note that we do not set a real
-                    # breakpoint here because we may not really need one; sub
-                    # scan_list will do that if necessary
-                    $line_start_index_to_go = $i_test + 1;
-                    $gnu_position_predictor = $test_position;
-                }
-            }
-        }
+                        # then make the switch -- note that we do not set a
+                        # real breakpoint here because we may not really need
+                        # one; sub break_lists will do that if necessary.
 
-        my $halfway =
-          $maximum_line_length_at_level[$level] -
-          $rOpts_maximum_line_length / 2;
+                        my $Kc = $K_closing_container->{$seqno};
+                        if (
 
-        # Check for decreasing depth ..
-        # Note that one token may have both decreasing and then increasing
-        # depth. For example, (level, ci) can go from (1,1) to (2,0).  So,
-        # in this example we would first go back to (1,0) then up to (2,0)
-        # in a single call.
-        if ( $level < $current_level || $ci_level < $current_ci_level ) {
+                            # For -lp, only if the closing token is in this
+                            # batch (c117).  Otherwise it cannot be done by sub
+                            # break_lists.
+                            defined($Kc) && $Kc <= $K_to_go[$max_index_to_go]
 
-            # loop to find the first entry at or completely below this level
-            my ( $lev, $ci_lev );
-            while (1) {
-                if ($max_gnu_stack_index) {
-
-                    # save index of token which closes this level
-                    $gnu_stack[$max_gnu_stack_index]
-                      ->set_closed($max_index_to_go);
-
-                    # Undo any extra indentation if we saw no commas
-                    my $available_spaces =
-                      $gnu_stack[$max_gnu_stack_index]->get_available_spaces();
-
-                    my $comma_count = 0;
-                    my $arrow_count = 0;
-                    if ( $type eq '}' || $type eq ')' ) {
-                        $comma_count = $gnu_comma_count{$total_depth};
-                        $arrow_count = $gnu_arrow_count{$total_depth};
-                        $comma_count = 0 unless $comma_count;
-                        $arrow_count = 0 unless $arrow_count;
-                    }
-                    $gnu_stack[$max_gnu_stack_index]
-                      ->set_comma_count($comma_count);
-                    $gnu_stack[$max_gnu_stack_index]
-                      ->set_arrow_count($arrow_count);
-
-                    if ( $available_spaces > 0 ) {
-
-                        if ( $comma_count <= 0 || $arrow_count > 0 ) {
-
-                            my $i =
-                              $gnu_stack[$max_gnu_stack_index]->get_index();
-                            my $seqno =
-                              $gnu_stack[$max_gnu_stack_index]
-                              ->get_sequence_number();
-
-                            # Be sure this item was created in this batch.  This
-                            # should be true because we delete any available
-                            # space from open items at the end of each batch.
-                            if (   $gnu_sequence_number != $seqno
-                                || $i > $max_gnu_item_index )
+                            # For -xlp, we only need one nonblank token after
+                            # the opening token.
+                            || $rOpts_extended_line_up_parentheses
+                          )
+                        {
+                            $ii_begin_line         = $i_test + 1;
+                            $lp_position_predictor = $test_position;
+
+                            #--------------------------------------------------
+                            # Fix for an opening container terminating a batch:
+                            #--------------------------------------------------
+                            # To get alignment of a -lp container with its
+                            # contents, we have to put a break after $i_test.
+                            # For $ii<$max_index_to_go, this will be done by
+                            # sub break_lists based on the indentation object.
+                            # But for $ii=$max_index_to_go, the indentation
+                            # object for this seqno will not be created until
+                            # the next batch, so we have to set a break at
+                            # $i_test right now in order to get one.
+                            if (   $ii == $max_index_to_go
+                                && !$block_type_to_go[$ii]
+                                && $type eq '{'
+                                && $seqno
+                                && !$ris_excluded_lp_container->{$seqno} )
                             {
-                                warning(
-"Program bug with -lp.  seqno=$seqno should be $gnu_sequence_number and i=$i should be less than max=$max_gnu_item_index\n"
-                                );
-                                report_definite_bug();
+                                $self->set_forced_lp_break( $ii_begin_line,
+                                    $ii );
+                            }
+                        }
+                    }
+                }
+            } ## end update position predictor
+
+            #------------------------
+            # Handle decreasing depth
+            #------------------------
+            # Note that one token may have both decreasing and then increasing
+            # depth. For example, (level, ci) can go from (1,1) to (2,0).  So,
+            # in this example we would first go back to (1,0) then up to (2,0)
+            # in a single call.
+            if ( $level < $current_level || $ci_level < $current_ci_level ) {
+
+                # loop to find the first entry at or completely below this level
+                my ( $lev, $ci_lev );
+                while (1) {
+                    if ($max_lp_stack) {
+
+                        # save index of token which closes this level
+                        if ( $rLP->[$max_lp_stack]->[_lp_object_] ) {
+                            my $lp_object =
+                              $rLP->[$max_lp_stack]->[_lp_object_];
+
+                            $lp_object->set_closed($ii);
+
+                            my $comma_count = 0;
+                            my $arrow_count = 0;
+                            if ( $type eq '}' || $type eq ')' ) {
+                                $comma_count = $lp_comma_count{$total_depth};
+                                $arrow_count = $lp_arrow_count{$total_depth};
+                                $comma_count = 0 unless $comma_count;
+                                $arrow_count = 0 unless $arrow_count;
                             }
 
-                            else {
-                                if ( $arrow_count == 0 ) {
-                                    $gnu_item_list[$i]
-                                      ->permanently_decrease_available_spaces(
-                                        $available_spaces);
+                            $lp_object->set_comma_count($comma_count);
+                            $lp_object->set_arrow_count($arrow_count);
+
+                            # Undo any extra indentation if we saw no commas
+                            my $available_spaces =
+                              $lp_object->get_available_spaces();
+                            my $K_start = $lp_object->get_K_begin_line();
+
+                            if (   $available_spaces > 0
+                                && $K_start >= $K_to_go[0]
+                                && ( $comma_count <= 0 || $arrow_count > 0 ) )
+                            {
+
+                                my $i = $lp_object->get_lp_item_index();
+
+                                # Safety check for a valid stack index. It
+                                # should be ok because we just checked that the
+                                # index K of the token associated with this
+                                # indentation is in this batch.
+                                if ( $i < 0 || $i > $max_lp_object_list ) {
+                                    if (DEVEL_MODE) {
+                                        my $lno = $rLL->[$KK]->[_LINE_INDEX_];
+                                        Fault(<<EOM);
+Program bug with -lp near line $lno.  Stack index i=$i should be >=0 and <= max=$max_lp_object_list
+EOM
+                                    }
                                 }
                                 else {
-                                    $gnu_item_list[$i]
-                                      ->tentatively_decrease_available_spaces(
-                                        $available_spaces);
-                                }
-                                foreach my $j ( $i + 1 .. $max_gnu_item_index )
-                                {
-                                    $gnu_item_list[$j]
-                                      ->decrease_SPACES($available_spaces);
+                                    if ( $arrow_count == 0 ) {
+                                        $rlp_object_list->[$i]
+                                          ->permanently_decrease_available_spaces
+                                          ($available_spaces);
+                                    }
+                                    else {
+                                        $rlp_object_list->[$i]
+                                          ->tentatively_decrease_available_spaces
+                                          ($available_spaces);
+                                    }
+                                    foreach
+                                      my $j ( $i + 1 .. $max_lp_object_list )
+                                    {
+                                        $rlp_object_list->[$j]
+                                          ->decrease_SPACES($available_spaces);
+                                    }
                                 }
                             }
                         }
+
+                        # go down one level
+                        --$max_lp_stack;
+
+                        my $rLP_top = $rLP->[$max_lp_stack];
+                        my $ci_lev  = $rLP_top->[_lp_ci_level_];
+                        my $lev     = $rLP_top->[_lp_level_];
+                        my $spaces  = $rLP_top->[_lp_space_count_];
+                        if ( $rLP_top->[_lp_object_] ) {
+                            my $lp_obj = $rLP_top->[_lp_object_];
+                            ( $spaces, $lev, $ci_lev ) =
+                              @{ $lp_obj->get_spaces_level_ci() };
+                        }
+
+                        # stop when we reach a level at or below the current
+                        # level
+                        if ( $lev <= $level && $ci_lev <= $ci_level ) {
+                            $space_count      = $spaces;
+                            $current_level    = $lev;
+                            $current_ci_level = $ci_lev;
+                            last;
+                        }
                     }
 
-                    # go down one level
-                    --$max_gnu_stack_index;
-                    $lev    = $gnu_stack[$max_gnu_stack_index]->get_level();
-                    $ci_lev = $gnu_stack[$max_gnu_stack_index]->get_ci_level();
-
-                    # stop when we reach a level at or below the current level
-                    if ( $lev <= $level && $ci_lev <= $ci_level ) {
-                        $space_count =
-                          $gnu_stack[$max_gnu_stack_index]->get_spaces();
-                        $current_level    = $lev;
-                        $current_ci_level = $ci_lev;
+                    # reached bottom of stack .. should never happen because
+                    # only negative levels can get here, and $level was forced
+                    # to be positive above.
+                    else {
+
+                        # non-fatal, keep going except in DEVEL_MODE
+                        if (DEVEL_MODE) {
+                            Fault(<<EOM);
+program bug with -lp: stack_error. level=$level; lev=$lev; ci_level=$ci_level; ci_lev=$ci_lev; rerun with -nlp
+EOM
+                        }
                         last;
                     }
                 }
+            } ## end decreasing depth
 
-                # reached bottom of stack .. should never happen because
-                # only negative levels can get here, and $level was forced
-                # to be positive above.
-                else {
-                    warning(
-"program bug with -lp: stack_error. level=$level; lev=$lev; ci_level=$ci_level; ci_lev=$ci_lev; rerun with -nlp\n"
-                    );
-                    report_definite_bug();
-                    last;
-                }
-            }
-        }
+            #------------------------
+            # handle increasing depth
+            #------------------------
+            if ( $level > $current_level || $ci_level > $current_ci_level ) {
 
-        # handle increasing depth
-        if ( $level > $current_level || $ci_level > $current_ci_level ) {
+                $stack_changed = 1;
 
-            # Compute the standard incremental whitespace.  This will be
-            # the minimum incremental whitespace that will be used.  This
-            # choice results in a smooth transition between the gnu-style
-            # and the standard style.
-            my $standard_increment =
-              ( $level - $current_level ) *
-              $rOpts_indent_columns +
-              ( $ci_level - $current_ci_level ) *
-              $rOpts_continuation_indentation;
+                # Compute the standard incremental whitespace.  This will be
+                # the minimum incremental whitespace that will be used.  This
+                # choice results in a smooth transition between the gnu-style
+                # and the standard style.
+                my $standard_increment =
+                  ( $level - $current_level ) *
+                  $rOpts_indent_columns +
+                  ( $ci_level - $current_ci_level ) *
+                  $rOpts_continuation_indentation;
 
-            # Now we have to define how much extra incremental space
-            # ("$available_space") we want.  This extra space will be
-            # reduced as necessary when long lines are encountered or when
-            # it becomes clear that we do not have a good list.
-            my $available_space = 0;
-            my $align_paren     = 0;
-            my $excess          = 0;
+                # Now we have to define how much extra incremental space
+                # ("$available_space") we want.  This extra space will be
+                # reduced as necessary when long lines are encountered or when
+                # it becomes clear that we do not have a good list.
+                my $available_spaces = 0;
+                my $align_seqno      = 0;
+                my $excess           = 0;
 
-            my $last_nonblank_seqno;
-            if ( defined($K_last_nonblank) ) {
-                $last_nonblank_seqno =
-                  $rLL->[$K_last_nonblank]->[_TYPE_SEQUENCE_];
-            }
+                my $last_nonblank_seqno;
+                my $last_nonblank_block_type;
+                if ( defined($K_last_nonblank) ) {
+                    $last_nonblank_seqno =
+                      $rLL->[$K_last_nonblank]->[_TYPE_SEQUENCE_];
+                    $last_nonblank_block_type =
+                        $last_nonblank_seqno
+                      ? $rblock_type_of_seqno->{$last_nonblank_seqno}
+                      : undef;
+                }
 
-            # initialization on empty stack..
-            if ( $max_gnu_stack_index == 0 ) {
-                $space_count = $level * $rOpts_indent_columns;
-            }
+                $in_lp_mode = $rLP->[$max_lp_stack]->[_lp_object_];
 
-            # if this is a BLOCK, add the standard increment
-            elsif ($last_nonblank_block_type) {
-                $space_count += $standard_increment;
-            }
+                #-----------------------------------------------
+                # Initialize indentation spaces on empty stack..
+                #-----------------------------------------------
+                if ( $max_lp_stack == 0 ) {
+                    $space_count = $level * $rOpts_indent_columns;
+                }
 
-            # add the standard increment for containers excluded by user rules
-            # or which contain here-docs or multiline qw text
-            elsif ( defined($last_nonblank_seqno)
-                && $ris_excluded_lp_container->{$last_nonblank_seqno} )
-            {
-                $space_count += $standard_increment;
-            }
+                #----------------------------------------
+                # Add the standard space increment if ...
+                #----------------------------------------
+                elsif (
 
-            # if last nonblank token was not structural indentation,
-            # just use standard increment
-            elsif ( $last_nonblank_type ne '{' ) {
-                $space_count += $standard_increment;
-            }
+                    # if this is a BLOCK, add the standard increment
+                    $last_nonblank_block_type
 
-            # otherwise use the space to the first non-blank level change token
-            else {
+                    # or if this is not a sequenced item
+                    || !$last_nonblank_seqno
 
-                $space_count = $gnu_position_predictor;
+                    # or this continer is excluded by user rules
+                    # or contains here-docs or multiline qw text
+                    || defined($last_nonblank_seqno)
+                    && $ris_excluded_lp_container->{$last_nonblank_seqno}
 
-                my $min_gnu_indentation =
-                  $gnu_stack[$max_gnu_stack_index]->get_spaces();
+                    # or if last nonblank token was not structural indentation
+                    || $last_nonblank_type ne '{'
 
-                $available_space = $space_count - $min_gnu_indentation;
-                if ( $available_space >= $standard_increment ) {
-                    $min_gnu_indentation += $standard_increment;
-                }
-                elsif ( $available_space > 1 ) {
-                    $min_gnu_indentation += $available_space + 1;
+                    # and do not start -lp under stress .. fixes b1244, b1255
+                    || !$in_lp_mode && $level >= $lp_cutoff_level
+
+                  )
+                {
+
+                    # If we have entered lp mode, use the top lp object to get
+                    # the current indentation spaces because it may have
+                    # changed.  Fixes b1285, b1286.
+                    if ($in_lp_mode) {
+                        $space_count = $in_lp_mode->get_spaces();
+                    }
+                    $space_count += $standard_increment;
                 }
-                elsif ( $last_nonblank_token =~ /^[\{\[\(]$/ ) {
-                    if ( ( $tightness{$last_nonblank_token} < 2 ) ) {
-                        $min_gnu_indentation += 2;
+
+                #---------------------------------------------------------------
+                # -lp mode: try to use space to the first non-blank level change
+                #---------------------------------------------------------------
+                else {
+
+                    # see how much space we have available
+                    my $test_space_count = $lp_position_predictor;
+                    my $excess           = 0;
+                    my $min_len =
+                      $rcollapsed_length_by_seqno->{$last_nonblank_seqno};
+                    my $next_opening_too_far;
+
+                    if ( defined($min_len) ) {
+                        $excess =
+                          $test_space_count +
+                          $min_len -
+                          $maximum_line_length_at_level[$level];
+                        if ( $excess > 0 ) {
+                            $test_space_count -= $excess;
+
+                            # will the next opening token be a long way out?
+                            $next_opening_too_far =
+                              $lp_position_predictor + $excess >
+                              $maximum_line_length_at_level[$level];
+                        }
+                    }
+
+                    my $rLP_top             = $rLP->[$max_lp_stack];
+                    my $min_gnu_indentation = $rLP_top->[_lp_space_count_];
+                    if ( $rLP_top->[_lp_object_] ) {
+                        $min_gnu_indentation =
+                          $rLP_top->[_lp_object_]->get_spaces();
                     }
+                    $available_spaces =
+                      $test_space_count - $min_gnu_indentation;
+
+                    # Do not startup -lp indentation mode if no space ...
+                    # ... or if it puts the opening far to the right
+                    if ( !$in_lp_mode
+                        && ( $available_spaces <= 0 || $next_opening_too_far ) )
+                    {
+                        $space_count += $standard_increment;
+                        $available_spaces = 0;
+                    }
+
+                    # Use -lp mode
                     else {
-                        $min_gnu_indentation += 1;
+                        $space_count = $test_space_count;
+
+                        $in_lp_mode = 1;
+                        if ( $available_spaces >= $standard_increment ) {
+                            $min_gnu_indentation += $standard_increment;
+                        }
+                        elsif ( $available_spaces > 1 ) {
+                            $min_gnu_indentation += $available_spaces + 1;
+                        }
+                        elsif ( $last_nonblank_token =~ /^[\{\[\(]$/ ) {
+                            if ( ( $tightness{$last_nonblank_token} < 2 ) ) {
+                                $min_gnu_indentation += 2;
+                            }
+                            else {
+                                $min_gnu_indentation += 1;
+                            }
+                        }
+                        else {
+                            $min_gnu_indentation += $standard_increment;
+                        }
+                        $available_spaces = $space_count - $min_gnu_indentation;
+
+                        if ( $available_spaces < 0 ) {
+                            $space_count      = $min_gnu_indentation;
+                            $available_spaces = 0;
+                        }
+                        $align_seqno = $last_nonblank_seqno;
                     }
                 }
-                else {
-                    $min_gnu_indentation += $standard_increment;
-                }
-                $available_space = $space_count - $min_gnu_indentation;
 
-                if ( $available_space < 0 ) {
-                    $space_count     = $min_gnu_indentation;
-                    $available_space = 0;
+                #-------------------------------------------
+                # update the state, but not on a blank token
+                #-------------------------------------------
+                if ( $type ne 'b' ) {
+
+                    if ( $rLP->[$max_lp_stack]->[_lp_object_] ) {
+                        $rLP->[$max_lp_stack]->[_lp_object_]->set_have_child(1);
+                        $in_lp_mode = 1;
+                    }
+
+                    #----------------------------------------
+                    # Create indentation object if in lp-mode
+                    #----------------------------------------
+                    ++$max_lp_stack;
+                    my $lp_object;
+                    if ($in_lp_mode) {
+
+                        # A negative level implies not to store the item in the
+                        # item_list
+                        my $lp_item_index = 0;
+                        if ( $level >= 0 ) {
+                            $lp_item_index = ++$max_lp_object_list;
+                        }
+
+                        my $K_begin_line = 0;
+                        if (   $ii_begin_line >= 0
+                            && $ii_begin_line <= $max_index_to_go )
+                        {
+                            $K_begin_line = $K_to_go[$ii_begin_line];
+                        }
+
+                        # Minor Fix: when creating indentation at a side
+                        # comment we don't know what the space to the actual
+                        # next code token will be.  We will allow a space for
+                        # sub correct_lp to move it in if necessary.
+                        if (   $type eq '#'
+                            && $max_index_to_go > 0
+                            && $align_seqno )
+                        {
+                            $available_spaces += 1;
+                        }
+
+                        $lp_object = Perl::Tidy::IndentationItem->new(
+                            spaces           => $space_count,
+                            level            => $level,
+                            ci_level         => $ci_level,
+                            available_spaces => $available_spaces,
+                            lp_item_index    => $lp_item_index,
+                            align_seqno      => $align_seqno,
+                            stack_depth      => $max_lp_stack,
+                            K_begin_line     => $K_begin_line,
+                        );
+
+                        DEBUG_LP && do {
+                            my $tok_beg = $rLL->[$K_begin_line]->[_TOKEN_];
+                            print STDERR <<EOM;
+DEBUG_LP: Created object at tok=$token type=$type for seqno $align_seqno level=$level ci=$ci_level spaces=$space_count avail=$available_spaces kbeg=$K_begin_line tokbeg=$tok_beg lp=$lp_position_predictor
+EOM
+                        };
+
+                        if ( $level >= 0 ) {
+                            $rlp_object_list->[$max_lp_object_list] =
+                              $lp_object;
+                        }
+
+                        if (   $last_nonblank_token =~ /^[\{\[\(]$/
+                            && $last_nonblank_seqno )
+                        {
+                            $rlp_object_by_seqno->{$last_nonblank_seqno} =
+                              $lp_object;
+                        }
+                    }
+
+                    #------------------------------------
+                    # Store this indentation on the stack
+                    #------------------------------------
+                    $rLP->[$max_lp_stack]->[_lp_ci_level_] = $ci_level;
+                    $rLP->[$max_lp_stack]->[_lp_level_]    = $level;
+                    $rLP->[$max_lp_stack]->[_lp_object_]   = $lp_object;
+                    $rLP->[$max_lp_stack]->[_lp_container_seqno_] =
+                      $last_nonblank_seqno;
+                    $rLP->[$max_lp_stack]->[_lp_space_count_] = $space_count;
+
+                    # If the opening paren is beyond the half-line length, then
+                    # we will use the minimum (standard) indentation.  This will
+                    # help avoid problems associated with running out of space
+                    # near the end of a line.  As a result, in deeply nested
+                    # lists, there will be some indentations which are limited
+                    # to this minimum standard indentation. But the most deeply
+                    # nested container will still probably be able to shift its
+                    # parameters to the right for proper alignment, so in most
+                    # cases this will not be noticeable.
+                    if ( $available_spaces > 0 && $lp_object ) {
+                        my $halfway =
+                          $maximum_line_length_at_level[$level] -
+                          $rOpts_maximum_line_length / 2;
+                        $lp_object->tentatively_decrease_available_spaces(
+                            $available_spaces)
+                          if ( $space_count > $halfway );
+                    }
                 }
-                $align_paren = 1;
-            }
+            } ## end increasing depth
 
-            # update state, but not on a blank token
-            if ( $types_to_go[$max_index_to_go] ne 'b' ) {
+            #------------------
+            # Handle all tokens
+            #------------------
+            if ( $type ne 'b' ) {
 
-                $gnu_stack[$max_gnu_stack_index]->set_have_child(1);
+                # Count commas and look for non-list characters.  Once we see a
+                # non-list character, we give up and don't look for any more
+                # commas.
+                if ( $type eq '=>' ) {
+                    $lp_arrow_count{$total_depth}++;
 
-                ++$max_gnu_stack_index;
-                $gnu_stack[$max_gnu_stack_index] =
-                  new_lp_indentation_item( $space_count, $level, $ci_level,
-                    $available_space, $align_paren );
+                    # remember '=>' like '=' for estimating breaks (but see
+                    # above note for b1035)
+                    $last_lp_equals{$total_depth} = $ii;
+                }
 
-                # If the opening paren is beyond the half-line length, then
-                # we will use the minimum (standard) indentation.  This will
-                # help avoid problems associated with running out of space
-                # near the end of a line.  As a result, in deeply nested
-                # lists, there will be some indentations which are limited
-                # to this minimum standard indentation. But the most deeply
-                # nested container will still probably be able to shift its
-                # parameters to the right for proper alignment, so in most
-                # cases this will not be noticeable.
-                if ( $available_space > 0 && $space_count > $halfway ) {
-                    $gnu_stack[$max_gnu_stack_index]
-                      ->tentatively_decrease_available_spaces($available_space);
+                elsif ( $type eq ',' ) {
+                    $lp_comma_count{$total_depth}++;
                 }
-            }
-        }
 
-        # Count commas and look for non-list characters.  Once we see a
-        # non-list character, we give up and don't look for any more commas.
-        if ( $type eq '=>' ) {
-            $gnu_arrow_count{$total_depth}++;
+                elsif ( $is_assignment{$type} ) {
+                    $last_lp_equals{$total_depth} = $ii;
+                }
 
-            # remember '=>' like '=' for estimating breaks (but see above note
-            # for b1035)
-            $last_gnu_equals{$total_depth} = $max_index_to_go;
-        }
+                # this token might start a new line if ..
+                if (
 
-        elsif ( $type eq ',' ) {
-            $gnu_comma_count{$total_depth}++;
-        }
+                    # this is the first nonblank token of the line
+                    $ii == 1 && $types_to_go[0] eq 'b'
 
-        elsif ( $is_assignment{$type} ) {
-            $last_gnu_equals{$total_depth} = $max_index_to_go;
-        }
+                    # or previous character was one of these:
+                    #  /^([\:\?\,f])$/
+                    || $hash_test2{$last_nonblank_type}
 
-        # this token might start a new line
-        # if this is a non-blank..
-        if ( $type ne 'b' ) {
+                    # or previous character was opening and this is not closing
+                    || ( $last_nonblank_type eq '{' && $type ne '}' )
+                    || ( $last_nonblank_type eq '(' and $type ne ')' )
 
-            # and if ..
-            if (
+                    # or this token is one of these:
+                    #  /^([\.]|\|\||\&\&)$/
+                    || $hash_test3{$type}
+
+                    # or this is a closing structure
+                    || (   $last_nonblank_type eq '}'
+                        && $last_nonblank_token eq $last_nonblank_type )
+
+                    # or previous token was keyword 'return'
+                    || (
+                        $last_nonblank_type eq 'k'
+                        && (   $last_nonblank_token eq 'return'
+                            && $type ne '{' )
+                    )
 
-                # this is the first nonblank token of the line
-                $max_index_to_go == 1 && $types_to_go[0] eq 'b'
+                    # or starting a new line at certain keywords is fine
+                    || (   $type eq 'k'
+                        && $is_if_unless_and_or_last_next_redo_return{$token} )
+
+                    # or this is after an assignment after a closing structure
+                    || (
+                        $is_assignment{$last_nonblank_type}
+                        && (
+                            # /^[\}\)\]]$/
+                            $hash_test1{$last_last_nonblank_type}
+
+                            # and it is significantly to the right
+                            || $lp_position_predictor > (
+                                $maximum_line_length_at_level[$level] -
+                                  $rOpts_maximum_line_length / 2
+                            )
+                        )
+                    )
+                  )
+                {
+                    check_for_long_gnu_style_lines( $ii, $rlp_object_list );
+                    $ii_begin_line = $ii;
 
-                # or previous character was one of these:
-                || $last_nonblank_type_in_batch =~ /^([\:\?\,f])$/
+                    # back up 1 token if we want to break before that type
+                    # otherwise, we may strand tokens like '?' or ':' on a line
+                    if ( $ii_begin_line > 0 ) {
+                        if ( $last_nonblank_type eq 'k' ) {
 
-                # or previous character was opening and this does not close it
-                || ( $last_nonblank_type_in_batch eq '{' && $type ne '}' )
-                || ( $last_nonblank_type_in_batch eq '(' and $type ne ')' )
+                            if ( $want_break_before{$last_nonblank_token} ) {
+                                $ii_begin_line--;
+                            }
+                        }
+                        elsif ( $want_break_before{$last_nonblank_type} ) {
+                            $ii_begin_line--;
+                        }
+                    }
+                } ## end if ( $ii == 1 && $types_to_go...)
 
-                # or this token is one of these:
-                || $type =~ /^([\.]|\|\||\&\&)$/
+                $K_last_nonblank = $KK;
 
-                # or this is a closing structure
-                || (   $last_nonblank_type_in_batch eq '}'
-                    && $last_nonblank_token_in_batch eq
-                    $last_nonblank_type_in_batch )
+                $last_last_nonblank_type = $last_nonblank_type;
+                $last_nonblank_type      = $type;
+                $last_nonblank_token     = $token;
 
-                # or previous token was keyword 'return'
-                || (
-                    $last_nonblank_type_in_batch eq 'k'
-                    && (   $last_nonblank_token_in_batch eq 'return'
-                        && $type ne '{' )
-                )
+            } ## end if ( $type ne 'b' )
 
-                # or starting a new line at certain keywords is fine
-                || (   $type eq 'k'
-                    && $is_if_unless_and_or_last_next_redo_return{$token} )
+            # remember the predicted position of this token on the output line
+            if ( $ii > $ii_begin_line ) {
 
-                # or this is after an assignment after a closing structure
-                || (
-                    $is_assignment{$last_nonblank_type_in_batch}
-                    && (
-                        $last_last_nonblank_type_in_batch =~ /^[\}\)\]]$/
+                ## NOTE: this is a critical loop - the following call has been
+                ## expanded for about 2x speedup:
+                ## $lp_position_predictor =
+                ##    total_line_length( $ii_begin_line, $ii );
 
-                        # and it is significantly to the right
-                        || $gnu_position_predictor > $halfway
-                    )
-                )
-              )
-            {
-                check_for_long_gnu_style_lines($max_index_to_go);
-                $line_start_index_to_go = $max_index_to_go;
+                my $indentation = $leading_spaces_to_go[$ii_begin_line];
+                if ( ref($indentation) ) {
+                    $indentation = $indentation->get_spaces();
+                }
+                $lp_position_predictor =
+                  $indentation +
+                  $summed_lengths_to_go[ $ii + 1 ] -
+                  $summed_lengths_to_go[$ii_begin_line];
+            }
+            else {
+                $lp_position_predictor =
+                  $space_count + $token_lengths_to_go[$ii];
+            }
 
-                # back up 1 token if we want to break before that type
-                # otherwise, we may strand tokens like '?' or ':' on a line
-                if ( $line_start_index_to_go > 0 ) {
-                    if ( $last_nonblank_type_in_batch eq 'k' ) {
+            # Store the indentation object for this token.
+            # This allows us to manipulate the leading whitespace
+            # (in case we have to reduce indentation to fit a line) without
+            # having to change any token values.
 
-                        if ( $want_break_before{$last_nonblank_token_in_batch} )
-                        {
-                            $line_start_index_to_go--;
-                        }
-                    }
-                    elsif ( $want_break_before{$last_nonblank_type_in_batch} ) {
-                        $line_start_index_to_go--;
-                    }
+            #---------------------------------------------------------------
+            # replace leading whitespace with indentation objects where used
+            #---------------------------------------------------------------
+            if ( $rLP->[$max_lp_stack]->[_lp_object_] ) {
+                my $lp_object = $rLP->[$max_lp_stack]->[_lp_object_];
+                $leading_spaces_to_go[$ii] = $lp_object;
+                if (   $max_lp_stack > 0
+                    && $ci_level
+                    && $rLP->[ $max_lp_stack - 1 ]->[_lp_object_] )
+                {
+                    $reduced_spaces_to_go[$ii] =
+                      $rLP->[ $max_lp_stack - 1 ]->[_lp_object_];
+                }
+                else {
+                    $reduced_spaces_to_go[$ii] = $lp_object;
                 }
             }
-        }
+        } ## end loop over all tokens in this batch
+
+        undo_incomplete_lp_indentation($rlp_object_list)
+          if ( !$rOpts_extended_line_up_parentheses );
 
-        # remember the predicted position of this token on the output line
-        if ( $max_index_to_go > $line_start_index_to_go ) {
-            $gnu_position_predictor =
-              total_line_length( $line_start_index_to_go, $max_index_to_go );
-        }
-        else {
-            $gnu_position_predictor =
-              $space_count + $token_lengths_to_go[$max_index_to_go];
-        }
-
-        # store the indentation object for this token
-        # this allows us to manipulate the leading whitespace
-        # (in case we have to reduce indentation to fit a line) without
-        # having to change any token values
-        $leading_spaces_to_go[$max_index_to_go] =
-          $gnu_stack[$max_gnu_stack_index];
-        $reduced_spaces_to_go[$max_index_to_go] =
-          ( $max_gnu_stack_index > 0 && $ci_level )
-          ? $gnu_stack[ $max_gnu_stack_index - 1 ]
-          : $gnu_stack[$max_gnu_stack_index];
         return;
     }
 
@@ -18628,19 +21590,18 @@ sub get_available_spaces_to_go {
 
         # look at the current estimated maximum line length, and
         # remove some whitespace if it exceeds the desired maximum
-        my ($mx_index_to_go) = @_;
+        my ( $mx_index_to_go, $rlp_object_list ) = @_;
 
-        # this is only for the '-lp' style
-        return unless ($rOpts_line_up_parentheses);
+        my $max_lp_object_list = @{$rlp_object_list} - 1;
 
         # nothing can be done if no stack items defined for this line
-        return if ( $max_gnu_item_index == UNDEFINED_INDEX );
+        return if ( $max_lp_object_list < 0 );
 
         # see if we have exceeded the maximum desired line length
         # keep 2 extra free because they are needed in some cases
         # (result of trial-and-error testing)
         my $spaces_needed =
-          $gnu_position_predictor -
+          $lp_position_predictor -
           $maximum_line_length_at_level[ $levels_to_go[$mx_index_to_go] ] + 2;
 
         return if ( $spaces_needed <= 0 );
@@ -18653,8 +21614,8 @@ sub get_available_spaces_to_go {
         my $i;
 
         # loop over all whitespace items created for the current batch
-        for ( $i = 0 ; $i <= $max_gnu_item_index ; $i++ ) {
-            my $item = $gnu_item_list[$i];
+        for ( $i = 0 ; $i <= $max_lp_object_list ; $i++ ) {
+            my $item = $rlp_object_list->[$i];
 
             # item must still be open to be a candidate (otherwise it
             # cannot influence the current token)
@@ -18682,57 +21643,66 @@ sub get_available_spaces_to_go {
               : $available_spaces;
 
             # remove the incremental space from this item
-            $gnu_item_list[$i]->decrease_available_spaces($deleted_spaces);
+            $rlp_object_list->[$i]->decrease_available_spaces($deleted_spaces);
 
             my $i_debug = $i;
 
             # update the leading whitespace of this item and all items
             # that came after it
-            for ( ; $i <= $max_gnu_item_index ; $i++ ) {
+            for ( ; $i <= $max_lp_object_list ; $i++ ) {
 
-                my $old_spaces = $gnu_item_list[$i]->get_spaces();
+                my $old_spaces = $rlp_object_list->[$i]->get_spaces();
                 if ( $old_spaces >= $deleted_spaces ) {
-                    $gnu_item_list[$i]->decrease_SPACES($deleted_spaces);
+                    $rlp_object_list->[$i]->decrease_SPACES($deleted_spaces);
                 }
 
                 # shouldn't happen except for code bug:
                 else {
-                    my $level        = $gnu_item_list[$i_debug]->get_level();
-                    my $ci_level     = $gnu_item_list[$i_debug]->get_ci_level();
-                    my $old_level    = $gnu_item_list[$i]->get_level();
-                    my $old_ci_level = $gnu_item_list[$i]->get_ci_level();
-                    warning(
-"program bug with -lp: want to delete $deleted_spaces from item $i, but old=$old_spaces deleted: lev=$level ci=$ci_level  deleted: level=$old_level ci=$ci_level\n"
-                    );
-                    report_definite_bug();
+                    # non-fatal, keep going except in DEVEL_MODE
+                    if (DEVEL_MODE) {
+                        my $level = $rlp_object_list->[$i_debug]->get_level();
+                        my $ci_level =
+                          $rlp_object_list->[$i_debug]->get_ci_level();
+                        my $old_level = $rlp_object_list->[$i]->get_level();
+                        my $old_ci_level =
+                          $rlp_object_list->[$i]->get_ci_level();
+                        Fault(<<EOM);
+program bug with -lp: want to delete $deleted_spaces from item $i, but old=$old_spaces deleted: lev=$level ci=$ci_level  deleted: level=$old_level ci=$ci_level
+EOM
+                    }
                 }
             }
-            $gnu_position_predictor -= $deleted_spaces;
-            $spaces_needed          -= $deleted_spaces;
+            $lp_position_predictor -= $deleted_spaces;
+            $spaces_needed         -= $deleted_spaces;
             last unless ( $spaces_needed > 0 );
         }
         return;
     }
 
-    sub finish_lp_batch {
+    sub undo_incomplete_lp_indentation {
+
+        #------------------------------------------------------------------
+        # Undo indentation for all incomplete -lp indentation levels of the
+        # current batch unless -xlp is set.
+        #------------------------------------------------------------------
 
         # This routine is called once after each output stream batch is
-        # finished to undo indentation for all incomplete -lp
-        # indentation levels.  It is too risky to leave a level open,
-        # because then we can't backtrack in case of a long line to follow.
-        # This means that comments and blank lines will disrupt this
-        # indentation style.  But the vertical aligner may be able to
-        # get the space back if there are side comments.
-
-        # this is only for the 'lp' style
-        return unless ($rOpts_line_up_parentheses);
+        # finished to undo indentation for all incomplete -lp indentation
+        # levels.  If this routine is called then comments and blank lines will
+        # disrupt this indentation style.  In older versions of perltidy this
+        # was always done because it could cause problems otherwise, but recent
+        # improvements allow fairly good results to be obtained by skipping
+        # this step with the -xlp flag.
+        my ($rlp_object_list) = @_;
 
-        # nothing can be done if no stack items defined for this line
-        return if ( $max_gnu_item_index == UNDEFINED_INDEX );
+        my $max_lp_object_list = @{$rlp_object_list} - 1;
+
+        # nothing to do if no stack items defined for this line
+        return if ( $max_lp_object_list < 0 );
 
         # loop over all whitespace items created for the current batch
-        foreach my $i ( 0 .. $max_gnu_item_index ) {
-            my $item = $gnu_item_list[$i];
+        foreach my $i ( 0 .. $max_lp_object_list ) {
+            my $item = $rlp_object_list->[$i];
 
             # only look for open items
             next if ( $item->get_closed() >= 0 );
@@ -18743,26 +21713,94 @@ sub get_available_spaces_to_go {
             if ( $available_spaces > 0 ) {
 
                 # delete incremental space for this item
-                $gnu_item_list[$i]
+                $rlp_object_list->[$i]
                   ->tentatively_decrease_available_spaces($available_spaces);
 
                 # Reduce the total indentation space of any nodes that follow
                 # Note that any such nodes must necessarily be dependents
                 # of this node.
-                foreach ( $i + 1 .. $max_gnu_item_index ) {
-                    $gnu_item_list[$_]->decrease_SPACES($available_spaces);
+                foreach ( $i + 1 .. $max_lp_object_list ) {
+                    $rlp_object_list->[$_]->decrease_SPACES($available_spaces);
                 }
             }
         }
         return;
     }
-} ## end closure set_leading_whitespace
+} ## end closure set_lp_indentation
+
+#----------------------------------------------------------------------
+# sub to set a requested break before an opening container in -lp mode.
+#----------------------------------------------------------------------
+sub set_forced_lp_break {
+
+    my ( $self, $i_begin_line, $i_opening ) = @_;
+
+    # Given:
+    #   $i_begin_line = index of break in the _to_go arrays
+    #   $i_opening = index of the opening container
+
+    # Set any requested break at a token before this opening container
+    # token. This is often an '=' or '=>' but can also be things like
+    # '.', ',', 'return'.  It was defined by sub set_lp_indentation.
+
+    # Important:
+    #   For intact containers, call this at the closing token.
+    #   For broken containers, call this at the opening token.
+    # This will avoid needless breaks when it turns out that the
+    # container does not actually get broken.  This isn't known until
+    # the closing container for intact blocks.
+
+    return
+      if ( $i_begin_line < 0
+        || $i_begin_line > $max_index_to_go );
+
+    # Handle request to put a break break immediately before this token.
+    # We may not want to do that since we are also breaking after it.
+    if ( $i_begin_line == $i_opening ) {
+
+        # The following rules should be reviewed.  We may want to always
+        # allow the break.  If we do not do the break, the indentation
+        # may be off.
+
+        # RULE: don't break before it unless it is welded to a qw.
+        # This works well, but we may want to relax this to allow
+        # breaks in additional cases.
+        return
+          if ( !$self->[_rK_weld_right_]->{ $K_to_go[$i_opening] } );
+        return unless ( $types_to_go[$max_index_to_go] eq 'q' );
+    }
+
+    # Only break for breakpoints at the same
+    # indentation level as the opening paren
+    my $test1 = $nesting_depth_to_go[$i_opening];
+    my $test2 = $nesting_depth_to_go[$i_begin_line];
+    return if ( $test2 != $test1 );
+
+    # Back up at a blank (fixes case b932)
+    my $ibr = $i_begin_line - 1;
+    if (   $ibr > 0
+        && $types_to_go[$ibr] eq 'b' )
+    {
+        $ibr--;
+    }
+    if ( $ibr >= 0 ) {
+        my $i_nonblank = $self->set_forced_breakpoint($ibr);
+
+        # Crude patch to prevent sub recombine_breakpoints from undoing
+        # this break, especially after an '='.  It will leave old
+        # breakpoints alone. See c098/x045 for some examples.
+        if ( defined($i_nonblank) ) {
+            $old_breakpoint_to_go[$i_nonblank] = 1;
+        }
+    }
+    return;
+}
 
 sub reduce_lp_indentation {
 
     # reduce the leading whitespace at token $i if possible by $spaces_needed
     # (a large value of $spaces_needed will remove all excess space)
-    # NOTE: to be called from scan_list only for a sequence of tokens
+    # NOTE: to be called from break_lists only for a sequence of tokens
     # contained between opening and closing parens/braces/brackets
 
     my ( $self, $i, $spaces_wanted ) = @_;
@@ -18790,7 +21828,50 @@ sub reduce_lp_indentation {
 # CODE SECTION 13: Preparing batches for vertical alignment
 ###########################################################
 
-sub send_lines_to_vertical_aligner {
+sub check_convey_batch_input {
+
+    # Check for valid input to sub convey_batch_to_vertical_aligner.  An
+    # error here would most likely be due to an error in the calling
+    # routine 'sub grind_batch_of_CODE'.
+    my ( $self, $ri_first, $ri_last ) = @_;
+
+    if ( !defined($ri_first) || !defined($ri_last) ) {
+        Fault(<<EOM);
+Undefined line ranges ri_first and/r ri_last
+EOM
+    }
+
+    my $nmax       = @{$ri_first} - 1;
+    my $nmax_check = @{$ri_last} - 1;
+    if ( $nmax < 0 || $nmax_check < 0 || $nmax != $nmax_check ) {
+        Fault(<<EOM);
+Line range index error: nmax=$nmax but nmax_check=$nmax_check
+These should be equal and >=0
+EOM
+    }
+    my ( $ibeg, $iend );
+    foreach my $n ( 0 .. $nmax ) {
+        my $ibeg_m = $ibeg;
+        my $iend_m = $iend;
+        $ibeg = $ri_first->[$n];
+        $iend = $ri_last->[$n];
+        if ( $ibeg < 0 || $iend < $ibeg || $iend > $max_index_to_go ) {
+            Fault(<<EOM);
+Bad line range at line index $n of $nmax: ibeg=$ibeg, iend=$iend
+These should have iend >= ibeg and be in the range (0..$max_index_to_go)
+EOM
+        }
+        next if ( $n == 0 );
+        if ( $ibeg <= $iend_m ) {
+            Fault(<<EOM);
+Line ranges overlap: iend=$iend_m at line $n-1 but ibeg=$ibeg for line $n
+EOM
+        }
+    }
+    return;
+}
+
+sub convey_batch_to_vertical_aligner {
 
     my ($self) = @_;
 
@@ -18803,63 +21884,42 @@ sub send_lines_to_vertical_aligner {
     #   logical constructions
 
     my $this_batch = $self->[_this_batch_];
-    my $rlines_K   = $this_batch->[_rlines_K_];
-    if ( !@{$rlines_K} ) {
+    my $ri_first   = $this_batch->[_ri_first_];
+    my $ri_last    = $this_batch->[_ri_last_];
 
-        # This can't happen because sub grind_batch_of_CODE always receives
-        # tokens which it turns into one or more lines. If we get here it means
-        # that a programming error has caused those lines to be lost.
-        Fault("Unexpected call with no lines");
-        return;
-    }
-    my $n_last_line = @{$rlines_K} - 1;
+    $self->check_convey_batch_input( $ri_first, $ri_last ) if (DEVEL_MODE);
+
+    my $n_last_line = @{$ri_first} - 1;
 
     my $do_not_pad               = $this_batch->[_do_not_pad_];
     my $peak_batch_size          = $this_batch->[_peak_batch_size_];
     my $starting_in_quote        = $this_batch->[_starting_in_quote_];
     my $ending_in_quote          = $this_batch->[_ending_in_quote_];
     my $is_static_block_comment  = $this_batch->[_is_static_block_comment_];
-    my $ibeg0                    = $this_batch->[_ibeg0_];
-    my $rK_to_go                 = $this_batch->[_rK_to_go_];
-    my $batch_count              = $this_batch->[_batch_count_];
     my $rix_seqno_controlling_ci = $this_batch->[_rix_seqno_controlling_ci_];
+    my $batch_CODE_type          = $this_batch->[_batch_CODE_type_];
 
-    my $rLL    = $self->[_rLL_];
-    my $Klimit = $self->[_Klimit_];
+    my $rLL                  = $self->[_rLL_];
+    my $Klimit               = $self->[_Klimit_];
+    my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
+    my $ris_list_by_seqno    = $self->[_ris_list_by_seqno_];
 
-    my ( $Kbeg_next, $Kend_next ) = @{ $rlines_K->[0] };
-    my $type_beg_next  = $rLL->[$Kbeg_next]->[_TYPE_];
-    my $token_beg_next = $rLL->[$Kbeg_next]->[_TOKEN_];
-    my $type_end_next  = $rLL->[$Kend_next]->[_TYPE_];
-
-    # Construct indexes to the global_to_go arrays so that called routines can
-    # still access those arrays. This might eventually be removed
-    # when all called routines have been converted to access token values
-    # in the rLL array instead.
-    my $Kbeg0 = $Kbeg_next;
-    my ( $ri_first, $ri_last );
-    foreach my $rline ( @{$rlines_K} ) {
-        my ( $Kbeg, $Kend ) = @{$rline};
-        my $ibeg = $ibeg0 + $Kbeg - $Kbeg0;
-        my $iend = $ibeg0 + $Kend - $Kbeg0;
-        push @{$ri_first}, $ibeg;
-        push @{$ri_last},  $iend;
-    }
+    my $ibeg_next = $ri_first->[0];
+    my $iend_next = $ri_last->[0];
+
+    my $type_beg_next  = $types_to_go[$ibeg_next];
+    my $type_end_next  = $types_to_go[$iend_next];
+    my $token_beg_next = $tokens_to_go[$ibeg_next];
+
+    my $is_block_comment = $max_index_to_go == 0 && $types_to_go[0] eq '#';
 
+    my $rindentation_list = [0];    # ref to indentations for each line
     my ( $cscw_block_comment, $closing_side_comment );
-    if ( $rOpts->{'closing-side-comments'} ) {
+    if ($rOpts_closing_side_comments) {
         ( $closing_side_comment, $cscw_block_comment ) =
-          $self->add_closing_side_comment();
+          $self->add_closing_side_comment( $ri_first, $ri_last );
     }
 
-    my $rindentation_list = [0];    # ref to indentations for each line
-
-    # define the array @{$ralignment_type_to_go} for the output tokens
-    # which will be non-blank for each special token (such as =>)
-    # for which alignment is required.
-    my $ralignment_type_to_go =
-      $self->set_vertical_alignment_markers( $ri_first, $ri_last );
-
     # flush before a long if statement to avoid unwanted alignment
     if (   $n_last_line > 0
         && $type_beg_next eq 'k'
@@ -18868,147 +21928,233 @@ sub send_lines_to_vertical_aligner {
         $self->flush_vertical_aligner();
     }
 
-    $self->undo_ci( $ri_first, $ri_last, $rix_seqno_controlling_ci );
+    $self->undo_ci( $ri_first, $ri_last, $rix_seqno_controlling_ci )
+      if ( $n_last_line > 0 || $rOpts_extended_continuation_indentation );
 
     $self->set_logical_padding( $ri_first, $ri_last, $peak_batch_size,
         $starting_in_quote )
-      if ( $rOpts->{'logical-padding'} );
+      if ( $n_last_line > 0 && $rOpts_logical_padding );
 
-    # Resum lengths. We need accurate lengths for making alignment patterns,
-    # and we may have unmasked a semicolon which was not included at the start.
-    for ( 0 .. $max_index_to_go ) {
-        $summed_lengths_to_go[ $_ + 1 ] =
-          $summed_lengths_to_go[$_] + $token_lengths_to_go[$_];
-    }
+    if (DEVEL_MODE) { $self->check_batch_summed_lengths() }
 
-    # loop to prepare each line for shipment
-    my ( $Kbeg, $type_beg, $token_beg );
-    my ( $Kend, $type_end );
+    # ----------------------------------------------------------
+    # define the vertical alignments for all lines of this batch
+    # ----------------------------------------------------------
+    my $rline_alignments =
+      $self->make_vertical_alignments( $ri_first, $ri_last );
+
+    # ----------------------------------------------
+    # loop to send each line to the vertical aligner
+    # ----------------------------------------------
+    my ( $type_beg, $token_beg );
+    my ($type_end);
+    my ( $ibeg, $iend );
     for my $n ( 0 .. $n_last_line ) {
 
-        my $ibeg              = $ri_first->[$n];
-        my $iend              = $ri_last->[$n];
-        my $rline             = $rlines_K->[$n];
-        my $forced_breakpoint = $rline->[2];
+        # ----------------------------------------------------------------
+        # This hash will hold the args for vertical alignment of this line
+        # We will populate it as we go.
+        # ----------------------------------------------------------------
+        my $rvao_args = {};
 
-        # we may need to look at variables on three consecutive lines ...
+        my $type_beg_last = $type_beg;
+        my $type_end_last = $type_end;
 
-        # Some vars on line [n-1], if any:
-        my $Kbeg_last      = $Kbeg;
-        my $type_beg_last  = $type_beg;
-        my $token_beg_last = $token_beg;
-        my $Kend_last      = $Kend;
-        my $type_end_last  = $type_end;
+        my $ibeg = $ibeg_next;
+        my $iend = $iend_next;
+        my $Kbeg = $K_to_go[$ibeg];
+        my $Kend = $K_to_go[$iend];
 
-        # Some vars on line [n]:
-        $Kbeg      = $Kbeg_next;
         $type_beg  = $type_beg_next;
-        $token_beg = $token_beg_next;
-        $Kend      = $Kend_next;
         $type_end  = $type_end_next;
+        $token_beg = $token_beg_next;
 
-        # Only forward ending K values of non-comments down the pipeline.
-        # This is equivalent to checking that the last CODE_type is blank or
-        # equal to 'VER'. See also sub resync_lines_and_tokens for related
-        # coding.  Note that '$batch_CODE_type' is the code type of the line
-        # to which the ending token belongs.
-        my $batch_CODE_type = $this_batch->[_batch_CODE_type_];
+        # ---------------------------------------------------
+        # Define the check value 'Kend' to send for this line
+        # ---------------------------------------------------
+        # The 'Kend' value is an integer for checking that lines come out of
+        # the far end of the pipeline in the right order.  It increases
+        # linearly along the token stream.  But we only send ending K values of
+        # non-comments down the pipeline.  This is equivalent to checking that
+        # the last CODE_type is blank or equal to 'VER'. See also sub
+        # resync_lines_and_tokens for related coding.  Note that
+        # '$batch_CODE_type' is the code type of the line to which the ending
+        # token belongs.
         my $Kend_code =
           $batch_CODE_type && $batch_CODE_type ne 'VER' ? undef : $Kend;
 
-        # We use two slightly different definitions of level jump at the end
-        # of line:
-        #  $ljump is the level jump needed by 'sub set_adjusted_indentation'
-        #  $level_jump is the level jump needed by the vertical aligner.
-        my $ljump = 0;    # level jump at end of line
+        #  $ljump is a level jump needed by 'sub final_indentation_adjustment'
+        my $ljump = 0;
 
         # Get some vars on line [n+1], if any:
         if ( $n < $n_last_line ) {
-            ( $Kbeg_next, $Kend_next ) =
-              @{ $rlines_K->[ $n + 1 ] };
-            $type_beg_next  = $rLL->[$Kbeg_next]->[_TYPE_];
-            $token_beg_next = $rLL->[$Kbeg_next]->[_TOKEN_];
-            $type_end_next  = $rLL->[$Kend_next]->[_TYPE_];
+            $ibeg_next = $ri_first->[ $n + 1 ];
+            $iend_next = $ri_last->[ $n + 1 ];
+
+            $type_beg_next  = $types_to_go[$ibeg_next];
+            $type_end_next  = $types_to_go[$iend_next];
+            $token_beg_next = $tokens_to_go[$ibeg_next];
+
+            my $Kbeg_next = $K_to_go[$ibeg_next];
             $ljump = $rLL->[$Kbeg_next]->[_LEVEL_] - $rLL->[$Kend]->[_LEVEL_];
         }
-        else {
+        elsif ( !$is_block_comment && $Kend < $Klimit ) {
 
             # Patch for git #51, a bare closing qw paren was not outdented
             # if the flag '-nodelete-old-newlines is set
-            my $Kbeg_next = $self->K_next_code($Kend);
-            if ( defined($Kbeg_next) ) {
-                $ljump =
-                  $rLL->[$Kbeg_next]->[_LEVEL_] - $rLL->[$Kend]->[_LEVEL_];
+            # Note that we are just looking ahead for the next nonblank
+            # character. We could scan past an arbitrary number of block
+            # comments or hanging side comments by calling K_next_code, but it
+            # could add significant run time with very little to be gained.
+            my $Kbeg_next = $Kend + 1;
+            if (   $Kbeg_next < $Klimit
+                && $rLL->[$Kbeg_next]->[_TYPE_] eq 'b' )
+            {
+                $Kbeg_next += 1;
             }
+            $ljump =
+              $rLL->[$Kbeg_next]->[_LEVEL_] - $rLL->[$Kend]->[_LEVEL_];
         }
 
-        # level jump at end of line for the vertical aligner:
-        my $level_jump =
-          $Kend >= $Klimit
-          ? 0
-          : $rLL->[ $Kend + 1 ]->[_SLEVEL_] - $rLL->[$Kbeg]->[_SLEVEL_];
-
-        $self->delete_needless_alignments( $ibeg, $iend,
-            $ralignment_type_to_go );
+        # ---------------------------------------------
+        # get the vertical alignment info for this line
+        # ---------------------------------------------
 
+        # The lines are broken into fields which can be spaced by the vertical
+        # to achieve vertical alignment.  These fields are the actual text
+        # which will be output, so from here on no more changes can be made to
+        # the text.
+        my $rline_alignment = $rline_alignments->[$n];
         my ( $rtokens, $rfields, $rpatterns, $rfield_lengths ) =
-          $self->make_alignment_patterns( $ibeg, $iend,
-            $ralignment_type_to_go );
+          @{$rline_alignment};
+
+        # Programming check: (shouldn't happen)
+        # The number of tokens which separate the fields must always be
+        # one less than the number of fields. If this is not true then
+        # an error has been introduced in sub make_alignment_patterns.
+        if (DEVEL_MODE) {
+            if ( @{$rfields} && ( @{$rtokens} != ( @{$rfields} - 1 ) ) ) {
+                my $nt  = @{$rtokens};
+                my $nf  = @{$rfields};
+                my $msg = <<EOM;
+Program bug in Perl::Tidy::Formatter, probably in sub 'make_alignment_patterns':
+The number of tokens = $nt should be one less than number of fields: $nf
+EOM
+                Fault($msg);
+            }
+        }
 
+        # --------------------------------------
+        # get the final indentation of this line
+        # --------------------------------------
         my ( $indentation, $lev, $level_end, $terminal_type,
             $terminal_block_type, $is_semicolon_terminated, $is_outdented_line )
-          = $self->set_adjusted_indentation( $ibeg, $iend, $rfields,
+          = $self->final_indentation_adjustment( $ibeg, $iend, $rfields,
             $rpatterns,         $ri_first, $ri_last,
             $rindentation_list, $ljump,    $starting_in_quote,
             $is_static_block_comment, );
 
-        # we will allow outdenting of long lines..
-        my $outdent_long_lines = (
-
+        # --------------------------------
+        # define flag 'outdent_long_lines'
+        # --------------------------------
+        if (
+            # we will allow outdenting of long lines..
             # which are long quotes, if allowed
-            ( $type_beg eq 'Q' && $rOpts->{'outdent-long-quotes'} )
+            ( $type_beg eq 'Q' && $rOpts_outdent_long_quotes )
 
             # which are long block comments, if allowed
-              || (
+            || (
                    $type_beg eq '#'
-                && $rOpts->{'outdent-long-comments'}
+                && $rOpts_outdent_long_comments
 
                 # but not if this is a static block comment
                 && !$is_static_block_comment
-              )
-        );
+            )
+          )
+        {
+            $rvao_args->{outdent_long_lines} = 1;
 
-        my $break_alignment_before = $is_outdented_line || $do_not_pad;
-        my $break_alignment_after  = $is_outdented_line;
+            # convert -lp indentation objects to spaces to allow outdenting
+            if ( ref($indentation) ) {
+                $indentation = $indentation->get_spaces();
+            }
+        }
+
+        # --------------------------------------------------
+        # define flags 'break_alignment_before' and '_after'
+        # --------------------------------------------------
+
+        # These flags tell the vertical aligner to stop alignment before or
+        # after this line.
+        if ($is_outdented_line) {
+            $rvao_args->{break_alignment_before} = 1;
+            $rvao_args->{break_alignment_after}  = 1;
+        }
+        elsif ($do_not_pad) {
+            $rvao_args->{break_alignment_before} = 1;
+        }
 
         # flush at an 'if' which follows a line with (1) terminal semicolon
         # or (2) terminal block_type which is not an 'if'.  This prevents
         # unwanted alignment between the lines.
-        if ( $type_beg eq 'k' && $token_beg eq 'if' ) {
-            my $Km           = $self->K_previous_code($Kbeg);
-            my $type_m       = 'b';
-            my $block_type_m = 'b';
-            if ( defined($Km) ) {
-                $type_m       = $rLL->[$Km]->[_TYPE_];
-                $block_type_m = $rLL->[$Km]->[_BLOCK_TYPE_];
+        elsif ( $type_beg eq 'k' && $token_beg eq 'if' ) {
+            my $type_m = 'b';
+            my $block_type_m;
+
+            if ( $Kbeg > 0 ) {
+                my $Km = $Kbeg - 1;
+                $type_m = $rLL->[$Km]->[_TYPE_];
+                if ( $type_m eq 'b' && $Km > 0 ) {
+                    $Km -= 1;
+                    $type_m = $rLL->[$Km]->[_TYPE_];
+                }
+                if ( $type_m eq '#' && $Km > 0 ) {
+                    $Km -= 1;
+                    $type_m = $rLL->[$Km]->[_TYPE_];
+                    if ( $type_m eq 'b' && $Km > 0 ) {
+                        $Km -= 1;
+                        $type_m = $rLL->[$Km]->[_TYPE_];
+                    }
+                }
+
+                my $seqno_m = $rLL->[$Km]->[_TYPE_SEQUENCE_];
+                if ($seqno_m) {
+                    $block_type_m = $rblock_type_of_seqno->{$seqno_m};
+                }
             }
 
             # break after anything that is not if-like
-            $break_alignment_before ||= $type_m eq ';'
-              || ( $type_m eq '}'
-                && $block_type_m ne 'if'
-                && $block_type_m ne 'unless'
-                && $block_type_m ne 'elsif'
-                && $block_type_m ne 'else' );
+            if (
+                $type_m eq ';'
+                || (   $type_m eq '}'
+                    && $block_type_m
+                    && $block_type_m ne 'if'
+                    && $block_type_m ne 'unless'
+                    && $block_type_m ne 'elsif'
+                    && $block_type_m ne 'else' )
+              )
+            {
+                $rvao_args->{break_alignment_before} = 1;
+            }
         }
 
-        my $rvertical_tightness_flags =
+        # ----------------------------------
+        # define 'rvertical_tightness_flags'
+        # ----------------------------------
+        # These flags tell the vertical aligner if/when to combine consecutive
+        # lines, based on the user input parameters.
+        $rvao_args->{rvertical_tightness_flags} =
           $self->set_vertical_tightness_flags( $n, $n_last_line, $ibeg, $iend,
-            $ri_first, $ri_last, $ending_in_quote, $closing_side_comment );
+            $ri_first, $ri_last, $ending_in_quote, $closing_side_comment )
+          if ( !$is_block_comment );
 
-        # Set a flag at the final ':' of a ternary chain to request
-        # vertical alignment of the final term.  Here is a
-        # slightly complex example:
+        # ----------------------------------
+        # define 'is_terminal_ternary'  flag
+        # ----------------------------------
+
+        # This flag is set at the final ':' of a ternary chain to request
+        # vertical alignment of the final term.  Here is a slightly complex
+        # example:
         #
         # $self->{_text} = (
         #    !$section        ? ''
@@ -19021,14 +22167,15 @@ sub send_lines_to_vertical_aligner {
         #   : ' elsewhere in this document'
         # );
         #
-        my $is_terminal_ternary = 0;
-
         if ( $type_beg eq ':' || $n > 0 && $type_end_last eq ':' ) {
-            my $last_leading_type = $n > 0 ? $type_beg_last : ':';
+
+            my $is_terminal_ternary = 0;
+            my $last_leading_type   = $n > 0 ? $type_beg_last : ':';
             if (   $terminal_type ne ';'
                 && $n_last_line > $n
                 && $level_end == $lev )
             {
+                my $Kbeg_next = $K_to_go[$ibeg_next];
                 $level_end     = $rLL->[$Kbeg_next]->[_LEVEL_];
                 $terminal_type = $rLL->[$Kbeg_next]->[_TYPE_];
             }
@@ -19057,65 +22204,111 @@ sub send_lines_to_vertical_aligner {
                     $KP = $rLL->[$KP]->[_KNEXT_SEQ_ITEM_];
                 }
             }
+            $rvao_args->{is_terminal_ternary} = $is_terminal_ternary;
         }
 
-        my $level_adj        = $lev;
-        my $radjusted_levels = $self->[_radjusted_levels_];
-        if ( defined($radjusted_levels) && @{$radjusted_levels} == @{$rLL} ) {
-            $level_adj = $radjusted_levels->[$Kbeg];
-            if ( $level_adj < 0 ) { $level_adj = 0 }
-        }
-
+        # -------------------------------------------------
         # add any new closing side comment to the last line
+        # -------------------------------------------------
         if ( $closing_side_comment && $n == $n_last_line && @{$rfields} ) {
+
             $rfields->[-1] .= " $closing_side_comment";
 
             # NOTE: Patch for csc. We can just use 1 for the length of the csc
             # because its length should not be a limiting factor from here on.
             $rfield_lengths->[-1] += 2;
+
+            # repack
+            $rline_alignment =
+              [ $rtokens, $rfields, $rpatterns, $rfield_lengths ];
         }
 
-        # Programming check: (shouldn't happen)
-        # The number of tokens which separate the fields must always be
-        # one less than the number of fields. If this is not true then
-        # an error has been introduced in sub make_alignment_patterns.
-        if ( @{$rfields} && ( @{$rtokens} != ( @{$rfields} - 1 ) ) ) {
-            my $nt  = @{$rtokens};
-            my $nf  = @{$rfields};
-            my $msg = <<EOM;
-Program bug in Perl::Tidy::Formatter, probably in sub 'make_alignment_patterns':
-The number of tokens = $nt should be one less than number of fields: $nf
-EOM
-            Fault($msg);
-        }
-
-        # Set flag which tells if this line is contained in a multi-line list
-        my $list_seqno = $self->is_list_by_K($Kbeg);
-
-        # send this new line down the pipe
-        my $rvalign_hash = {};
-        $rvalign_hash->{level}                     = $lev;
-        $rvalign_hash->{level_end}                 = $level_end;
-        $rvalign_hash->{level_adj}                 = $level_adj;
-        $rvalign_hash->{indentation}               = $indentation;
-        $rvalign_hash->{list_seqno}                = $list_seqno;
-        $rvalign_hash->{outdent_long_lines}        = $outdent_long_lines;
-        $rvalign_hash->{is_terminal_ternary}       = $is_terminal_ternary;
-        $rvalign_hash->{rvertical_tightness_flags} = $rvertical_tightness_flags;
-        $rvalign_hash->{level_jump}                = $level_jump;
-        $rvalign_hash->{rfields}                   = $rfields;
-        $rvalign_hash->{rpatterns}                 = $rpatterns;
-        $rvalign_hash->{rtokens}                   = $rtokens;
-        $rvalign_hash->{rfield_lengths}            = $rfield_lengths;
-        $rvalign_hash->{terminal_block_type}       = $terminal_block_type;
-        $rvalign_hash->{batch_count}               = $batch_count;
-        $rvalign_hash->{break_alignment_before}    = $break_alignment_before;
-        $rvalign_hash->{break_alignment_after}     = $break_alignment_after;
-        $rvalign_hash->{Kend}                      = $Kend_code;
-        $rvalign_hash->{ci_level}                  = $ci_levels_to_go[$ibeg];
+        # ------------------------
+        # define flag 'list_seqno'
+        # ------------------------
+
+        # This flag indicates if this line is contained in a multi-line list
+        if ( !$is_block_comment ) {
+            my $parent_seqno = $parent_seqno_to_go[$ibeg];
+            $rvao_args->{list_seqno} = $ris_list_by_seqno->{$parent_seqno};
+        }
+
+        # The alignment tokens have been marked with nesting_depths, so we need
+        # to pass nesting depths to the vertical aligner. They remain invariant
+        # under all formatting operations.  Previously, level values were sent
+        # to the aligner.  But they can be altered in welding and other
+        # opeartions, and this can lead to alignement errors.
+        my $nesting_depth_beg = $nesting_depth_to_go[$ibeg];
+        my $nesting_depth_end = $nesting_depth_to_go[$iend];
+
+        # A quirk in the definition of nesting depths is that the closing token
+        # has the same depth as internal tokens.  The vertical aligner is
+        # programmed to expect them to have the lower depth, so we fix this.
+        if ( $is_closing_type{ $types_to_go[$ibeg] } ) { $nesting_depth_beg-- }
+        if ( $is_closing_type{ $types_to_go[$iend] } ) { $nesting_depth_end-- }
+
+        # Adjust nesting depths to keep -lp indentation for qw lists.  This is
+        # required because qw lists contained in brackets do not get nesting
+        # depths, but the vertical aligner is watching nesting depth changes to
+        # decide if a -lp block is intact.  Without this patch, qw lists
+        # enclosed in angle brackets will not get the correct -lp indentation.
+
+        # Looking for line with isolated qw ...
+        if (   $rOpts_line_up_parentheses
+            && $type_beg eq 'q'
+            && $ibeg == $iend )
+        {
+
+            # ... which is part of a multiline qw
+            my $Km = $self->K_previous_nonblank($Kbeg);
+            my $Kp = $self->K_next_nonblank($Kbeg);
+            if (   defined($Km) && $rLL->[$Km]->[_TYPE_] eq 'q'
+                || defined($Kp) && $rLL->[$Kp]->[_TYPE_] eq 'q' )
+            {
+                $nesting_depth_beg++;
+                $nesting_depth_end++;
+            }
+        }
 
+        # ---------------------------------
+        # define flag 'forget_side_comment'
+        # ---------------------------------
+
+        # This flag tells the vertical aligner to reset the side comment
+        # location if we are entering a new block from level 0.  This is
+        # intended to keep side comments from drifting too far to the right.
+        if (   $terminal_block_type
+            && $nesting_depth_end > $nesting_depth_beg )
+        {
+            my $level_adj        = $lev;
+            my $radjusted_levels = $self->[_radjusted_levels_];
+            if ( defined($radjusted_levels) && @{$radjusted_levels} == @{$rLL} )
+            {
+                $level_adj = $radjusted_levels->[$Kbeg];
+                if ( $level_adj < 0 ) { $level_adj = 0 }
+            }
+            if ( $level_adj == 0 ) {
+                $rvao_args->{forget_side_comment} = 1;
+            }
+        }
+
+        # -----------------------------------
+        # Store the remaining non-flag values
+        # -----------------------------------
+        $rvao_args->{Kend}            = $Kend_code;
+        $rvao_args->{ci_level}        = $ci_levels_to_go[$ibeg];
+        $rvao_args->{indentation}     = $indentation;
+        $rvao_args->{level_end}       = $nesting_depth_end;
+        $rvao_args->{level}           = $nesting_depth_beg;
+        $rvao_args->{rline_alignment} = $rline_alignment;
+        $rvao_args->{maximum_line_length} =
+          $maximum_line_length_at_level[ $levels_to_go[$ibeg] ];
+
+        # --------------------------------------
+        # send this line to the vertical aligner
+        # --------------------------------------
         my $vao = $self->[_vertical_aligner_object_];
-        $vao->valign_input($rvalign_hash);
+        $vao->valign_input($rvao_args);
 
         $do_not_pad = 0;
 
@@ -19148,11 +22341,12 @@ EOM
           # and limit total to 10 character widths
           && token_sequence_length( $ibeg, $iend ) <= 10;
 
-    }    # end of loop to output each line
+    } ## end of loop to output each line
 
     # remember indentation of lines containing opening containers for
-    # later use by sub set_adjusted_indentation
-    $self->save_opening_indentation( $ri_first, $ri_last, $rindentation_list );
+    # later use by sub final_indentation_adjustment
+    $self->save_opening_indentation( $ri_first, $ri_last, $rindentation_list )
+      if ( !$is_block_comment );
 
     # output any new -cscw block comment
     if ($cscw_block_comment) {
@@ -19163,6 +22357,42 @@ EOM
     return;
 }
 
+sub check_batch_summed_lengths {
+
+    my ( $self, $msg ) = @_;
+    $msg = "" unless defined($msg);
+    my $rLL = $self->[_rLL_];
+
+    # Verify that the summed lengths are correct. We want to be sure that
+    # errors have not been introduced by programming changes.  Summed lengths
+    # are defined in sub $store_token.  Operations like padding and unmasking
+    # semicolons can change token lengths, but those operations are expected to
+    # update the summed lengths when they make changes.  So the summed lengths
+    # should always be correct.
+    foreach my $i ( 0 .. $max_index_to_go ) {
+        my $len_by_sum =
+          $summed_lengths_to_go[ $i + 1 ] - $summed_lengths_to_go[$i];
+        my $len_tok_i = $token_lengths_to_go[$i];
+        my $KK        = $K_to_go[$i];
+        my $len_tok_K;
+        if ( defined($KK) ) { $len_tok_K = $rLL->[$KK]->[_TOKEN_LENGTH_] }
+        if ( $len_by_sum != $len_tok_i
+            || defined($len_tok_K) && $len_by_sum != $len_tok_K )
+        {
+            my $lno = defined($KK) ? $rLL->[$KK]->[_LINE_INDEX_] + 1 : "undef";
+            $KK = 'undef' unless defined($KK);
+            my $tok  = $tokens_to_go[$i];
+            my $type = $types_to_go[$i];
+            Fault(<<EOM);
+Summed lengths are appear to be incorrect.  $msg
+lengths disagree: token length by sum=$len_by_sum but token_length_to_go[$i] = $len_tok_i and rLL->[$KK]->[_TOKEN_LENGTH_]=$len_tok_K
+near line $lno starting with '$tokens_to_go[0]..' at token i=$i K=$KK token_type='$type' token='$tok'
+EOM
+        }
+    }
+    return;
+}
+
 {    ## begin closure set_vertical_alignment_markers
     my %is_vertical_alignment_type;
     my %is_not_vertical_alignment_token;
         # accept vertical alignment.
 
         my ( $self, $ri_first, $ri_last ) = @_;
-        my $rspecial_side_comment_type = $self->[_rspecial_side_comment_type_];
 
         my $ralignment_type_to_go;
+        my $ralignment_counts       = [];
+        my $ralignment_hash_by_line = [];
 
-        # Initialize the alignment array. Note that closing side comments can
-        # insert up to 2 additional tokens beyond the original
-        # $max_index_to_go, so we need to check ri_last for the last index.
+        # NOTE: closing side comments can insert up to 2 additional tokens
+        # beyond the original $max_index_to_go, so we need to check ri_last for
+        # the last index.
         my $max_line = @{$ri_first} - 1;
-        my $iend     = $ri_last->[$max_line];
-        if ( $iend < $max_index_to_go ) { $iend = $max_index_to_go }
+        my $max_i    = $ri_last->[$max_line];
+        if ( $max_i < $max_index_to_go ) { $max_i = $max_index_to_go }
 
-        # nothing to do if we aren't allowed to change whitespace
-        # or there is only 1 token
-        if ( $iend == 0 || !$rOpts_add_whitespace ) {
-            for my $i ( 0 .. $iend ) {
-                $ralignment_type_to_go->[$i] = '';
-            }
-            return $ralignment_type_to_go;
+        # -----------------------------------------------------------------
+        # Shortcut:
+        #    - no alignments if there is only 1 token.
+        #    - and nothing to do if we aren't allowed to change whitespace.
+        # -----------------------------------------------------------------
+        if ( $max_i <= 0 || !$rOpts_add_whitespace ) {
+            return ( $ralignment_type_to_go, $ralignment_counts,
+                $ralignment_hash_by_line );
         }
 
-        # remember the index of last nonblank token before any sidecomment
-        my $i_terminal = $max_index_to_go;
-        if ( $types_to_go[$i_terminal] eq '#' ) {
-            if ( $i_terminal > 0 && $types_to_go[ --$i_terminal ] eq 'b' ) {
-                if ( $i_terminal > 0 ) { --$i_terminal }
+        my $rspecial_side_comment_type = $self->[_rspecial_side_comment_type_];
+        my $ris_function_call_paren    = $self->[_ris_function_call_paren_];
+        my $rLL                        = $self->[_rLL_];
+
+        # -------------------------------
+        # First handle any side comment.
+        # -------------------------------
+        my $i_terminal = $max_i;
+        if ( $types_to_go[$max_i] eq '#' ) {
+
+            # We know $max_i > 0 if we get here.
+            $i_terminal -= 1;
+            if ( $i_terminal > 0 && $types_to_go[$i_terminal] eq 'b' ) {
+                $i_terminal -= 1;
+            }
+
+            my $token = $tokens_to_go[$max_i];
+            my $KK    = $K_to_go[$max_i];
+
+            # Do not align various special side comments
+            my $do_not_align = (
+
+                # it is any specially marked side comment
+                ( defined($KK) && $rspecial_side_comment_type->{$KK} )
+
+                # or it is a static side comment
+                  || ( $rOpts->{'static-side-comments'}
+                    && $token =~ /$static_side_comment_pattern/ )
+
+                  # or a closing side comment
+                  || ( $types_to_go[$i_terminal] eq '}'
+                    && $tokens_to_go[$i_terminal] eq '}'
+                    && $token =~ /$closing_side_comment_prefix_pattern/ )
+            );
+
+            # - For the specific combination -vc -nvsc, we put all side comments
+            #   at fixed locations. Note that we will lose hanging side comment
+            #   alignments. Otherwise, hsc's can move to strange locations.
+            # - For -nvc -nvsc we make all side comments vertical alignments
+            #   because the vertical aligner will check for -nvsc and be able
+            #   to reduce the final padding to the side comments for long lines.
+            #   and keep hanging side comments aligned.
+            if (   !$do_not_align
+                && !$rOpts_valign_side_comments
+                && $rOpts_valign_code )
+            {
+
+                $do_not_align = 1;
+                my $ipad = $max_i - 1;
+                if ( $types_to_go[$ipad] eq 'b' ) {
+                    my $pad_spaces =
+                      $rOpts->{'minimum-space-to-comment'} -
+                      $token_lengths_to_go[$ipad];
+                    $self->pad_token( $ipad, $pad_spaces );
+                }
+            }
+
+            if ( !$do_not_align ) {
+                $ralignment_type_to_go->[$max_i] = '#';
+                $ralignment_hash_by_line->[$max_line]->{$max_i} = '#';
+                $ralignment_counts->[$max_line]++;
             }
         }
 
-        # look at each line of this batch..
-        my $last_vertical_alignment_before_index;
+        # ----------------------------------------------
+        # Nothing more to do on this line if -nvc is set
+        # ----------------------------------------------
+        if ( !$rOpts_valign_code ) {
+            return ( $ralignment_type_to_go, $ralignment_counts,
+                $ralignment_hash_by_line );
+        }
+
+        # -------------------------------------
+        # Loop over each line of this batch ...
+        # -------------------------------------
+        my $last_vertical_alignment_BEFORE_index;
         my $vert_last_nonblank_type;
         my $vert_last_nonblank_token;
         my $vert_last_nonblank_block_type;
 
         foreach my $line ( 0 .. $max_line ) {
+
             my $ibeg = $ri_first->[$line];
             my $iend = $ri_last->[$line];
-            $last_vertical_alignment_before_index = -1;
-            $vert_last_nonblank_type              = '';
-            $vert_last_nonblank_token             = '';
-            $vert_last_nonblank_block_type        = '';
 
-            # look at each token in this output line..
+            next if ( $iend <= $ibeg );
+
+            # back up before any side comment
+            if ( $iend > $i_terminal ) { $iend = $i_terminal }
+
             my $level_beg = $levels_to_go[$ibeg];
-            foreach my $i ( $ibeg .. $iend ) {
-                my $alignment_type = '';
+            my $token_beg = $tokens_to_go[$ibeg];
+            my $type_beg  = $types_to_go[$ibeg];
+            my $type_beg_special_char =
+              ( $type_beg eq '.' || $type_beg eq ':' || $type_beg eq '?' );
+
+            $last_vertical_alignment_BEFORE_index = -1;
+            $vert_last_nonblank_type              = $type_beg;
+            $vert_last_nonblank_token             = $token_beg;
+
+            # ----------------------------------------------------------------
+            # Initialization code merged from 'sub delete_needless_alignments'
+            # ----------------------------------------------------------------
+            my $i_good_paren  = -1;
+            my $i_elsif_close = $ibeg - 1;
+            my $i_elsif_open  = $iend + 1;
+            my @imatch_list;
+            if ( $type_beg eq 'k' ) {
+
+                # Initialization for paren patch: mark a location of a paren we
+                # should keep, such as one following something like a leading
+                # 'if', 'elsif',
+                $i_good_paren = $ibeg + 1;
+                if ( $types_to_go[$i_good_paren] eq 'b' ) {
+                    $i_good_paren++;
+                }
+
+                # Initializtion for 'elsif' patch: remember the paren range of
+                # an elsif, and do not make alignments within them because this
+                # can cause loss of padding and overall brace alignment in the
+                # vertical aligner.
+                if (   $token_beg eq 'elsif'
+                    && $i_good_paren < $iend
+                    && $tokens_to_go[$i_good_paren] eq '(' )
+                {
+                    $i_elsif_open  = $i_good_paren;
+                    $i_elsif_close = $mate_index_to_go[$i_good_paren];
+                }
+            } ## end if ( $type_beg eq 'k' )
+
+            # --------------------------------------------
+            # Loop over each token in this output line ...
+            # --------------------------------------------
+            foreach my $i ( $ibeg + 1 .. $iend ) {
+
+                next if ( $types_to_go[$i] eq 'b' );
+
                 my $type           = $types_to_go[$i];
-                my $block_type     = $block_type_to_go[$i];
                 my $token          = $tokens_to_go[$i];
+                my $alignment_type = '';
+
+                # ----------------------------------------------
+                # Check for 'paren patch' : Remove excess parens
+                # ----------------------------------------------
+
+                # Excess alignment of parens can prevent other good alignments.
+                # For example, note the parens in the first two rows of the
+                # following snippet.  They would normally get marked for
+                # alignment and aligned as follows:
+
+                #    my $w = $columns * $cell_w + ( $columns + 1 ) * $border;
+                #    my $h = $rows * $cell_h +    ( $rows + 1 ) * $border;
+                #    my $img = new Gimp::Image( $w, $h, RGB );
+
+                # This causes unnecessary paren alignment and prevents the
+                # third equals from aligning. If we remove the unwanted
+                # alignments we get:
+
+                #    my $w   = $columns * $cell_w + ( $columns + 1 ) * $border;
+                #    my $h   = $rows * $cell_h + ( $rows + 1 ) * $border;
+                #    my $img = new Gimp::Image( $w, $h, RGB );
+
+                # A rule for doing this which works well is to remove alignment
+                # of parens whose containers do not contain other aligning
+                # tokens, with the exception that we always keep alignment of
+                # the first opening paren on a line (for things like 'if' and
+                # 'elsif' statements).
+                if ( $token eq ')' && @imatch_list ) {
+
+                    # undo the corresponding opening paren if:
+                    # - it is at the top of the stack
+                    # - and not the first overall opening paren
+                    # - does not follow a leading keyword on this line
+                    my $imate = $mate_index_to_go[$i];
+                    if (   $imatch_list[-1] eq $imate
+                        && ( $ibeg > 1 || @imatch_list > 1 )
+                        && $imate > $i_good_paren )
+                    {
+                        if ( $ralignment_type_to_go->[$imate] ) {
+                            $ralignment_type_to_go->[$imate] = '';
+                            $ralignment_counts->[$line]--;
+                            delete $ralignment_hash_by_line->[$line]->{$imate};
+                        }
+                        pop @imatch_list;
+                    }
+                }
 
-                # do not align tokens at lower level then start of line
+                # do not align tokens at lower level than start of line
                 # except for side comments
-                if (   $levels_to_go[$i] < $levels_to_go[$ibeg]
-                    && $type ne '#' )
-                {
-                    $ralignment_type_to_go->[$i] = '';
+                if ( $levels_to_go[$i] < $level_beg ) {
                     next;
                 }
 
@@ -19283,34 +22669,9 @@ EOM
                 # must follow a blank token
                 elsif ( $types_to_go[ $i - 1 ] ne 'b' ) { }
 
-                # align a side comment --
-                elsif ( $type eq '#' ) {
-
-                    my $KK      = $K_to_go[$i];
-                    my $sc_type = $rspecial_side_comment_type->{$KK};
-
-                    unless (
-
-                        # it is any specially marked side comment
-                        $sc_type
-
-                        # or it is a static side comment
-                        || (   $rOpts->{'static-side-comments'}
-                            && $token =~ /$static_side_comment_pattern/ )
-
-                        # or a closing side comment
-                        || (   $vert_last_nonblank_block_type
-                            && $token =~
-                            /$closing_side_comment_prefix_pattern/ )
-                      )
-                    {
-                        $alignment_type = $type;
-                    }    ## Example of a static side comment
-                }
-
                 # otherwise, do not align two in a row to create a
                 # blank field
-                elsif ( $last_vertical_alignment_before_index == $i - 2 ) { }
+                elsif ( $last_vertical_alignment_BEFORE_index == $i - 2 ) { }
 
                 # align before one of these keywords
                 # (within a line, since $i>1)
@@ -19323,7 +22684,6 @@ EOM
                 }
 
                 # align before one of these types..
-                # Note: add '.' after new vertical aligner is operational
                 elsif ( $is_vertical_alignment_type{$type}
                     && !$is_not_vertical_alignment_token{$token} )
                 {
@@ -19337,7 +22697,7 @@ EOM
                     # nothing follows it, and
                     # (2) doing so may prevent other good alignments.
                     # Current exceptions are && and || and =>
-                    if ( $i == $iend || $i >= $i_terminal ) {
+                    if ( $i == $iend ) {
                         $alignment_type = ""
                           unless ( $is_terminal_alignment_type{$type} );
                     }
@@ -19359,13 +22719,9 @@ EOM
                     #     $PDL::IO::Pic::biggrays
                     #     ? ( m/GIF/          ? 0 : 1 )
                     #     : ( m/GIF|RAST|IFF/ ? 0 : 1 );
-                    if (
-                           $i == $ibeg + 2
-                        && $types_to_go[ $i - 1 ] eq 'b'
-                        && (   $types_to_go[$ibeg] eq '.'
-                            || $types_to_go[$ibeg] eq ':'
-                            || $types_to_go[$ibeg] eq '?' )
-                      )
+                    if (   $type_beg_special_char
+                        && $i == $ibeg + 2
+                        && $types_to_go[ $i - 1 ] eq 'b' )
                     {
                         $alignment_type = "";
                     }
@@ -19390,20 +22746,10 @@ EOM
                         }
 
                         # Do not align a spaced-function-paren if requested.
-                        # Issue git #53.  Note that $i-1 is a blank token if we
-                        # get here.
-                        if (  !$rOpts_function_paren_vertical_alignment
-                            && $i > $ibeg + 1 )
-                        {
-                            my $type_m  = $types_to_go[ $i - 2 ];
-                            my $token_m = $tokens_to_go[ $i - 2 ];
-
-                            # this is the same test as 'space-function-paren'
-                            if (   $type_m =~ /^[wUG]$/
-                                || $type_m eq '->'
-                                || $type_m  =~ /^[wi]$/
-                                && $token_m =~ /^(\&|->)/ )
-                            {
+                        # Issue git #53, #73.
+                        if ( !$rOpts_function_paren_vertical_alignment ) {
+                            my $seqno = $type_sequence_to_go[$i];
+                            if ( $ris_function_call_paren->{$seqno} ) {
                                 $alignment_type = "";
                             }
                         }
@@ -19420,7 +22766,7 @@ EOM
                 #{ $alignment_type = $type; }
 
                 if ($alignment_type) {
-                    $last_vertical_alignment_before_index = $i;
+                    $last_vertical_alignment_BEFORE_index = $i;
                 }
 
                 #--------------------------------------------------------
                 # We want to line up ',' and interior ';' tokens, with the added
                 # space AFTER these tokens.  (Note: interior ';' is included
                 # because it may occur in short blocks).
-                if (
+                elsif (
 
                     # we haven't already set it
-                    !$alignment_type
+                    ##!$alignment_type
+
+                    # previous token IS one of these:
+                    (
+                           $vert_last_nonblank_type eq ','
+                        || $vert_last_nonblank_type eq ';'
+                    )
 
                     # and its not the first token of the line
-                    && ( $i > $ibeg )
+                    ## && $i > $ibeg
 
                     # and it follows a blank
                     && $types_to_go[ $i - 1 ] eq 'b'
 
-                    # and previous token IS one of these:
-                    && (   $vert_last_nonblank_type eq ','
-                        || $vert_last_nonblank_type eq ';' )
-
                     # and it's NOT one of these
-                    && (   $type ne 'b'
-                        && $type ne '#'
-                        && !$is_closing_token{$type} )
+                    && !$is_closing_token{$type}
 
                     # then go ahead and align
                   )
 
-                {
-                    $alignment_type = $vert_last_nonblank_type;
-                }
+                {
+                    $alignment_type = $vert_last_nonblank_type;
+                }
+
+                #-----------------------
+                # Set the alignment type
+                #-----------------------
+                if ($alignment_type) {
+
+                    # but do not align the opening brace of an anonymous sub
+                    if (   $token eq '{'
+                        && $block_type_to_go[$i] =~ /$ASUB_PATTERN/ )
+                    {
+
+                    }
+
+                    # and do not make alignments within 'elsif' parens
+                    elsif ( $i > $i_elsif_open && $i < $i_elsif_close ) {
+
+                    }
 
-                #--------------------------------------------------------
-                # Undo alignment in special cases
-                #--------------------------------------------------------
-                if ($alignment_type) {
+                    # and ignore any tokens which have leading padded spaces
+                    # example: perl527/lop.t
+                    elsif ( substr( $alignment_type, 0, 1 ) eq ' ' ) {
 
-                    # do not align the opening brace of an anonymous sub
-                    if ( $token eq '{' && $block_type =~ /$ASUB_PATTERN/ ) {
-                        $alignment_type = "";
                     }
-                }
 
-                #--------------------------------------------------------
-                # then store the value
-                #--------------------------------------------------------
-                $ralignment_type_to_go->[$i] = $alignment_type;
-                if ( $type ne 'b' ) {
-                    $vert_last_nonblank_type       = $type;
-                    $vert_last_nonblank_token      = $token;
-                    $vert_last_nonblank_block_type = $block_type;
+                    else {
+                        $ralignment_type_to_go->[$i] = $alignment_type;
+                        $ralignment_hash_by_line->[$line]->{$i} =
+                          $alignment_type;
+                        $ralignment_counts->[$line]++;
+                        push @imatch_list, $i;
+                    }
                 }
+
+                $vert_last_nonblank_type  = $type;
+                $vert_last_nonblank_token = $token;
             }
         }
-        return $ralignment_type_to_go;
-    }
+
+        return ( $ralignment_type_to_go, $ralignment_counts,
+            $ralignment_hash_by_line );
+    } ## end sub set_vertical_alignment_markers
 } ## end closure set_vertical_alignment_markers
 
+sub make_vertical_alignments {
+    my ( $self, $ri_first, $ri_last ) = @_;
+
+    #----------------------------
+    # Shortcut for a single token
+    #----------------------------
+    if ( $max_index_to_go == 0 ) {
+        if ( @{$ri_first} == 1 && $ri_last->[0] == 0 ) {
+            my $rtokens   = [];
+            my $rfields   = [ $tokens_to_go[0] ];
+            my $rpatterns = [ $types_to_go[0] ];
+            my $rfield_lengths =
+              [ $summed_lengths_to_go[1] - $summed_lengths_to_go[0] ];
+            return [ [ $rtokens, $rfields, $rpatterns, $rfield_lengths ] ];
+        }
+
+        # Strange line packing, not fatal but should not happen
+        elsif (DEVEL_MODE) {
+            my $max_line = @{$ri_first} - 1;
+            my $ibeg     = $ri_first->[0];
+            my $iend     = $ri_last->[0];
+            my $tok_b    = $tokens_to_go[$ibeg];
+            my $tok_e    = $tokens_to_go[$iend];
+            my $type_b   = $types_to_go[$ibeg];
+            my $type_e   = $types_to_go[$iend];
+            Fault(
+"Strange..max_index=0 but nlines=$max_line ibeg=$ibeg tok=$tok_b type=$type_b iend=$iend tok=$tok_e type=$type_e; please check\n"
+            );
+        }
+    }
+
+    #---------------------------------------------------------
+    # Step 1: Define the alignment tokens for the entire batch
+    #---------------------------------------------------------
+    my ( $ralignment_type_to_go, $ralignment_counts, $ralignment_hash_by_line )
+      = $self->set_vertical_alignment_markers( $ri_first, $ri_last );
+
+    #----------------------------------------------
+    # Step 2: Break each line into alignment fields
+    #----------------------------------------------
+    my $rline_alignments = [];
+    my $max_line         = @{$ri_first} - 1;
+    foreach my $line ( 0 .. $max_line ) {
+
+        my $ibeg = $ri_first->[$line];
+        my $iend = $ri_last->[$line];
+
+        my $rtok_fld_pat_len = $self->make_alignment_patterns(
+            $ibeg, $iend, $ralignment_type_to_go,
+            $ralignment_counts->[$line],
+            $ralignment_hash_by_line->[$line]
+        );
+        push @{$rline_alignments}, $rtok_fld_pat_len;
+    }
+    return $rline_alignments;
+} ## end sub make_vertical_alignments
+
 sub get_seqno {
 
     # get opening and closing sequence numbers of a token for the vertical
@@ -19491,11 +22910,9 @@ sub get_seqno {
     # tokens by the vertical aligner.
     my ( $self, $ii, $ending_in_quote ) = @_;
 
-    my $rLL        = $self->[_rLL_];
-    my $this_batch = $self->[_this_batch_];
-    my $rK_to_go   = $this_batch->[_rK_to_go_];
+    my $rLL = $self->[_rLL_];
 
-    my $KK    = $rK_to_go->[$ii];
+    my $KK    = $K_to_go[$ii];
     my $seqno = $rLL->[$KK]->[_TYPE_SEQUENCE_];
 
     if ( $rLL->[$KK]->[_TYPE_] eq 'q' ) {
@@ -19553,10 +22970,23 @@ sub get_seqno {
 
         # Loop over all lines of the batch ...
 
-        # Workaround for problem c007, in which the combination -lp -xci
-        # can produce a "Program bug" message in unusual circumstances.
-        my $skip_SECTION_1 = $rOpts_line_up_parentheses
-          && $rOpts->{'extended-continuation-indentation'};
+        # Workaround originally created for problem c007, in which the
+        # combination -lp -xci could produce a "Program bug" message in unusual
+        # circumstances.
+        my $skip_SECTION_1;
+        if (   $rOpts_line_up_parentheses
+            && $rOpts_extended_continuation_indentation )
+        {
+
+            # Only set this flag if -lp is actually used here
+            foreach my $line ( 0 .. $max_line ) {
+                my $ibeg = $ri_first->[$line];
+                if ( ref( $leading_spaces_to_go[$ibeg] ) ) {
+                    $skip_SECTION_1 = 1;
+                    last;
+                }
+            }
+        }
 
         foreach my $line ( 0 .. $max_line ) {
 
@@ -19564,9 +22994,9 @@ sub get_seqno {
             my $iend = $ri_last->[$line];
             my $lev  = $levels_to_go[$ibeg];
 
-            ####################################
+            #-----------------------------------
             # SECTION 1: Undo needless common CI
-            ####################################
+            #-----------------------------------
 
             # We are looking at leading tokens and looking for a sequence all
             # at the same level and all at a higher level than enclosing lines.
@@ -19659,11 +23089,11 @@ sub get_seqno {
                 }
             }
 
-            ######################################
+            #-------------------------------------
             # SECTION 2: Undo ci at cuddled blocks
-            ######################################
+            #-------------------------------------
 
-            # Note that sub set_adjusted_indentation will be called later to
+            # Note that sub final_indentation_adjustment will be called later to
             # actually do this, but for now we will tentatively mark cuddled
             # lines with ci=0 so that the the -xci loop which follows will be
             # correct at cuddles.
@@ -19686,9 +23116,9 @@ sub get_seqno {
                 }
             }
 
-            #########################################################
+            #--------------------------------------------------------
             # SECTION 3: Undo ci set by sub extended_ci if not needed
-            #########################################################
+            #--------------------------------------------------------
 
             # Undo the ci of the leading token if its controlling token
             # went out on a previous line without ci
@@ -19762,7 +23192,7 @@ sub get_seqno {
         # This is needed to compensate for a change which was made in 'sub
         # starting_one_line_block' to prevent blinkers.  Previously, that sub
         # would not look at the total block size and rely on sub
-        # set_continuation_breaks to break up long blocks. Consequently, the
+        # break_long_lines to break up long blocks. Consequently, the
         # first line of those batches would end in the opening block brace of a
         # sort/map/grep/eval block.  When this was changed to immediately check
         # for blocks which were too long, the opening block brace would go out
@@ -19781,16 +23211,29 @@ sub get_seqno {
         #          : $cells[$b] <=> $cells[$a];   # batch n
         #      } ( 0 .. $#cells );                # batch n
 
-        my $rLL   = $self->[_rLL_];
-        my $K0    = $K_to_go[0];
-        my $Kprev = $self->K_previous_code($K0);
+        my $rLL                  = $self->[_rLL_];
+        my $rblock_type_of_seqno = $self->[_rblock_type_of_seqno_];
+
         my $is_short_block;
-        if ( defined($Kprev)
-            && $rLL->[$Kprev]->[_BLOCK_TYPE_] )
-        {
-            my $block_type = $rLL->[$Kprev]->[_BLOCK_TYPE_];
-            $is_short_block = $is_sort_map_grep_eval{$block_type};
-            $is_short_block ||= $want_one_line_block{$block_type};
+        if ( $K_to_go[0] > 0 ) {
+            my $Kp = $K_to_go[0] - 1;
+            if ( $Kp > 0 && $rLL->[$Kp]->[_TYPE_] eq 'b' ) {
+                $Kp -= 1;
+            }
+            if ( $Kp > 0 && $rLL->[$Kp]->[_TYPE_] eq '#' ) {
+                $Kp -= 1;
+                if ( $Kp > 0 && $rLL->[$Kp]->[_TYPE_] eq 'b' ) {
+                    $Kp -= 1;
+                }
+            }
+            my $seqno = $rLL->[$Kp]->[_TYPE_SEQUENCE_];
+            if ($seqno) {
+                my $block_type = $rblock_type_of_seqno->{$seqno};
+                if ($block_type) {
+                    $is_short_block = $is_sort_map_grep_eval{$block_type};
+                    $is_short_block ||= $want_one_line_block{$block_type};
+                }
+            }
         }
 
         # looking at each line of this batch..
@@ -20183,13 +23626,14 @@ sub get_seqno {
 
                 # make sure this won't change if -lp is used
                 my $indentation_1 = $leading_spaces_to_go[$ibeg];
-                if ( ref($indentation_1) ) {
-                    if ( $indentation_1->get_recoverable_spaces() == 0 ) {
-                        my $indentation_2 = $leading_spaces_to_go[$ibeg_next];
-                        unless ( $indentation_2->get_recoverable_spaces() == 0 )
-                        {
-                            $pad_spaces = 0;
-                        }
+                if ( ref($indentation_1)
+                    && $indentation_1->get_recoverable_spaces() == 0 )
+                {
+                    my $indentation_2 = $leading_spaces_to_go[$ibeg_next];
+                    if ( ref($indentation_2)
+                        && $indentation_2->get_recoverable_spaces() != 0 )
+                    {
+                        $pad_spaces = 0;
                     }
                 }
 
@@ -20228,7 +23672,7 @@ sub get_seqno {
             $iendm          = $iend;
             $ibegm          = $ibeg;
             $has_leading_op = $has_leading_op_next;
-        }    # end of loop over lines
+        } ## end of loop over lines
         return;
     }
 } ## end closure set_logical_padding
@@ -20270,25 +23714,19 @@ sub pad_token {
 
 {    ## begin closure make_alignment_patterns
 
-    my %block_type_map;
     my %keyword_map;
     my %operator_map;
     my %is_w_n_C;
+    my %is_my_local_our;
+    my %is_kwU;
+    my %is_use_like;
+    my %is_binary_type;
+    my %is_binary_keyword;
+    my %name_map;
 
     BEGIN {
 
-        # map related block names into a common name to
-        # allow alignment
-        %block_type_map = (
-            'unless'  => 'if',
-            'else'    => 'if',
-            'elsif'   => 'if',
-            'when'    => 'if',
-            'default' => 'if',
-            'case'    => 'if',
-            'sort'    => 'map',
-            'grep'    => 'map',
-        );
+        # Note: %block_type_map is now global to enable the -gal=s option
 
         # map certain keywords to the same 'if' class to align
         # long if/elsif sequences. [elsif.pl]
@@ -20318,111 +23756,53 @@ sub pad_token {
             'n' => 1,
             'C' => 1,
         );
-    }
-
-    sub delete_needless_alignments {
-        my ( $self, $ibeg, $iend, $ralignment_type_to_go ) = @_;
-
-        # Remove unwanted alignments.  This routine is a place to remove
-        # alignments which might cause problems at later stages.  There are
-        # currently two types of fixes:
-
-        # 1. Remove excess parens
-        # 2. Remove alignments within 'elsif' conditions
-
-        # Patch #1: Excess alignment of parens can prevent other good
-        # alignments.  For example, note the parens in the first two rows of
-        # the following snippet.  They would normally get marked for alignment
-        # and aligned as follows:
-
-        #    my $w = $columns * $cell_w + ( $columns + 1 ) * $border;
-        #    my $h = $rows * $cell_h +    ( $rows + 1 ) * $border;
-        #    my $img = new Gimp::Image( $w, $h, RGB );
-
-        # This causes unnecessary paren alignment and prevents the third equals
-        # from aligning. If we remove the unwanted alignments we get:
-
-        #    my $w   = $columns * $cell_w + ( $columns + 1 ) * $border;
-        #    my $h   = $rows * $cell_h + ( $rows + 1 ) * $border;
-        #    my $img = new Gimp::Image( $w, $h, RGB );
-
-        # A rule for doing this which works well is to remove alignment of
-        # parens whose containers do not contain other aligning tokens, with
-        # the exception that we always keep alignment of the first opening
-        # paren on a line (for things like 'if' and 'elsif' statements).
-
-        # Setup needed constants
-        my $i_good_paren  = -1;
-        my $imin_match    = $iend + 1;
-        my $i_elsif_close = $ibeg - 1;
-        my $i_elsif_open  = $iend + 1;
-        if ( $iend > $ibeg ) {
-            if ( $types_to_go[$ibeg] eq 'k' ) {
-
-                # Paren patch: mark a location of a paren we should keep, such
-                # as one following something like a leading 'if', 'elsif',..
-                $i_good_paren = $ibeg + 1;
-                if ( $types_to_go[$i_good_paren] eq 'b' ) {
-                    $i_good_paren++;
-                }
 
-                # 'elsif' patch: remember the range of the parens of an elsif,
-                # and do not make alignments within them because this can cause
-                # loss of padding and overall brace alignment in the vertical
-                # aligner.
-                if (   $tokens_to_go[$ibeg] eq 'elsif'
-                    && $i_good_paren < $iend
-                    && $tokens_to_go[$i_good_paren] eq '(' )
-                {
-                    $i_elsif_open  = $i_good_paren;
-                    $i_elsif_close = $mate_index_to_go[$i_good_paren];
-                }
-            }
-        }
+        # leading keywords which to skip for efficiency when making parenless
+        # container names
+        my @q = qw( my local our return );
+        @{is_my_local_our}{@q} = (1) x scalar(@q);
 
-        # Loop to make the fixes on this line
-        my @imatch_list;
-        for my $i ( $ibeg .. $iend ) {
-
-            if ( $ralignment_type_to_go->[$i] ) {
+        # leading keywords where we should just join one token to form
+        # parenless name
+        @q = qw( use );
+        @{is_use_like}{@q} = (1) x scalar(@q);
 
-                # Patch #2: undo alignment within elsif parens
-                if ( $i > $i_elsif_open && $i < $i_elsif_close ) {
-                    $ralignment_type_to_go->[$i] = '';
-                    next;
-                }
-                push @imatch_list, $i;
+        # leading token types which may be used to make a container name
+        @q = qw( k w U );
+        @{is_kwU}{@q} = (1) x scalar(@q);
 
-            }
-            if ( $tokens_to_go[$i] eq ')' ) {
+        # token types which prevent using leading word as a container name
+        @q = qw(
+          x / : % . | ^ < = > || >= != *= => !~ == && |= .= -= =~ += <= %= ^= x= ~~ ** << /=
+          &= // >> ~. &. |. ^.
+          **= <<= >>= &&= ||= //= <=> !~~ &.= |.= ^.= <<~
+        );
+        push @q, ',';
+        @{is_binary_type}{@q} = (1) x scalar(@q);
+
+        # token keywords which prevent using leading word as a container name
+        @_ = qw(and or err eq ne cmp);
+        @is_binary_keyword{@_} = (1) x scalar(@_);
+
+        # Some common function calls whose args can be aligned.  These do not
+        # give good alignments if the lengths differ significantly.
+        %name_map = (
+            'unlike' => 'like',
+            'isnt'   => 'is',
+            ##'is_deeply' => 'is',   # poor; names lengths too different
+        );
 
-                # Patch #1: undo the corresponding opening paren if:
-                # - it is at the top of the stack
-                # - and not the first overall opening paren
-                # - does not follow a leading keyword on this line
-                my $imate = $mate_index_to_go[$i];
-                if (   @imatch_list
-                    && $imatch_list[-1] eq $imate
-                    && ( $ibeg > 1 || @imatch_list > 1 )
-                    && $imate > $i_good_paren )
-                {
-                    $ralignment_type_to_go->[$imate] = '';
-                    pop @imatch_list;
-                }
-            }
-        }
-        return;
     }
 
     sub make_alignment_patterns {
 
         # Here we do some important preliminary work for the
-        # vertical aligner.  We create three arrays for one
+        # vertical aligner.  We create four arrays for one
         # output line. These arrays contain strings that can
         # be tested by the vertical aligner to see if
         # consecutive lines can be aligned vertically.
         #
-        # The three arrays are indexed on the vertical
+        # The four arrays are indexed on the vertical
         # alignment fields and are:
         # @tokens - a list of any vertical alignment tokens for this line.
         #   These are tokens, such as '=' '&&' '#' etc which
@@ -20435,24 +23815,153 @@ sub pad_token {
         # @patterns - a modified list of token types, one for each alignment
         #   field.  These should normally each match before alignment is
         #   allowed, even when the alignment tokens match.
-        my ( $self, $ibeg, $iend, $ralignment_type_to_go ) = @_;
+        # @field_lengths - the display width of each field
+
+        my ( $self, $ibeg, $iend, $ralignment_type_to_go, $alignment_count,
+            $ralignment_hash )
+          = @_;
+
+        # The var $ralignment_hash contains all of the alignments for this
+        # line.  It is not yet used but is available for future coding in case
+        # there is a need to do a preliminary scan of the alignment tokens.
+        if (DEVEL_MODE) {
+            my $new_count = 0;
+            if ( defined($ralignment_hash) ) {
+                $new_count = keys %{$ralignment_hash};
+            }
+            my $old_count = $alignment_count;
+            $old_count = 0 unless ($old_count);
+            if ( $new_count != $old_count ) {
+                my $K   = $K_to_go[$ibeg];
+                my $rLL = $self->[_rLL_];
+                my $lnl = $rLL->[$K]->[_LINE_INDEX_];
+                Fault(
+"alignment hash token count gives count=$new_count but old count is $old_count near line=$lnl\n"
+                );
+            }
+        }
+
+        # -------------------------------------
+        # Shortcut for lines without alignments
+        # -------------------------------------
+        if ( !$alignment_count ) {
+            my $rtokens        = [];
+            my $rfield_lengths = [ $summed_lengths_to_go[ $iend + 1 ] -
+                  $summed_lengths_to_go[$ibeg] ];
+            my $rpatterns;
+            my $rfields;
+            if ( $ibeg == $iend ) {
+                $rfields   = [ $tokens_to_go[$ibeg] ];
+                $rpatterns = [ $types_to_go[$ibeg] ];
+            }
+            else {
+                $rfields   = [ join( '', @tokens_to_go[ $ibeg .. $iend ] ) ];
+                $rpatterns = [ join( '', @types_to_go[ $ibeg .. $iend ] ) ];
+            }
+            return [ $rtokens, $rfields, $rpatterns, $rfield_lengths ];
+        }
+
+        my $i_start        = $ibeg;
+        my $depth          = 0;
+        my %container_name = ( 0 => "" );
+
         my @tokens        = ();
         my @fields        = ();
         my @patterns      = ();
         my @field_lengths = ();
-        my $i_start       = $ibeg;
 
-        # For a 'use' statement, use the module name as container name.
-        # Fixes issue rt136416.
-        my $cname = "";
-        if ( $types_to_go[$ibeg] eq 'k' && $tokens_to_go[$ibeg] eq 'use' ) {
-            my $inext = $inext_to_go[$ibeg];
-            if ( $inext <= $iend ) { $cname = $tokens_to_go[$inext] }
-        }
+        #-------------------------------------------------------------
+        # Make a container name for any uncontained commas, issue c089
+        #-------------------------------------------------------------
+        # This is a generalization of the fix for rt136416 which was a
+        # specialized patch just for 'use Module' statements.
+        # We restrict this to semicolon-terminated statements; that way
+        # we know that the top level commas are not in a list container.
+        if ( $ibeg == 0 && $iend == $max_index_to_go ) {
+            my $iterm = $max_index_to_go;
+            if ( $types_to_go[$iterm] eq '#' ) {
+                $iterm = $iprev_to_go[$iterm];
+            }
+
+            # Alignment lines ending like '=> sub {';  fixes issue c093
+            my $term_type_ok = $types_to_go[$iterm] eq ';';
+            $term_type_ok ||=
+              $tokens_to_go[$iterm] eq '{' && $block_type_to_go[$iterm];
+
+            if (   $iterm > $ibeg
+                && $term_type_ok
+                && !$is_my_local_our{ $tokens_to_go[$ibeg] }
+                && $levels_to_go[$ibeg] eq $levels_to_go[$iterm] )
+            {
 
-        my $depth          = 0;
-        my %container_name = ( 0 => "$cname" );
+                # Make a container name by combining all leading barewords,
+                # keywords and functions.
+                my $name  = "";
+                my $count = 0;
+                my $count_max;
+                my $iname_end;
+                my $ilast_blank;
+                for ( $ibeg .. $iterm ) {
+                    my $type = $types_to_go[$_];
+
+                    if ( $type eq 'b' ) {
+                        $ilast_blank = $_;
+                        next;
+                    }
+
+                    my $token = $tokens_to_go[$_];
+
+                    # Give up if we find an opening paren, binary operator or
+                    # comma within or after the proposed container name.
+                    if (   $token eq '('
+                        || $is_binary_type{$type}
+                        || $type eq 'k' && $is_binary_keyword{$token} )
+                    {
+                        $name = "";
+                        last;
+                    }
+
+                    # The container name is only built of certain types:
+                    last if ( !$is_kwU{$type} );
 
+                    # Normally it is made of one word, but two words for 'use'
+                    if ( $count == 0 ) {
+                        if (   $type eq 'k'
+                            && $is_use_like{ $tokens_to_go[$_] } )
+                        {
+                            $count_max = 2;
+                        }
+                        else {
+                            $count_max = 1;
+                        }
+                    }
+                    elsif ( defined($count_max) && $count >= $count_max ) {
+                        last;
+                    }
+
+                    if ( defined( $name_map{$token} ) ) {
+                        $token = $name_map{$token};
+                    }
+
+                    $name .= ' ' . $token;
+                    $iname_end = $_;
+                    $count++;
+                }
+
+                # Require a space after the container name token(s)
+                if (   $name
+                    && defined($ilast_blank)
+                    && $ilast_blank > $iname_end )
+                {
+                    $name = substr( $name, 1 );
+                    $container_name{'0'} = $name;
+                }
+            }
+        }
+
+        # --------------------
+        # Loop over all tokens
+        # --------------------
         my $j = 0;    # field index
 
         $patterns[0] = "";
@@ -20468,7 +23977,7 @@ sub pad_token {
             my $token      = $tokens_to_go[$i];
             my $depth_last = $depth;
             if ( $type_sequence_to_go[$i] ) {
-                if ( $is_opening_type{$token} ) {
+                if ( $is_opening_token{$token} ) {
 
                     # if container is balanced on this line...
                     my $i_mate = $mate_index_to_go[$i];
@@ -20483,7 +23992,7 @@ sub pad_token {
                      # Containers beginning with { and [ are given those names
                      # for uniqueness. That way commas in different containers
                      # will not match. Here is an example of what this prevents:
-                     # a => [ 1,       2, 3 ],
+                     #   a => [ 1,       2, 3 ],
                      #   b => { b1 => 4, b2 => 5 },
                      # Here is another example of what we avoid by labeling the
                      # commas properly:
@@ -20496,33 +24005,37 @@ sub pad_token {
                         if ( $token eq '(' ) {
                             $name = $self->make_paren_name($i);
                         }
+
+                        # name cannot be '.', so change to something else if so
+                        if ( $name eq '.' ) { $name = 'dot' }
+
                         $container_name{$depth} = "+" . $name;
 
-                       # Make the container name even more unique if necessary.
-                       # If we are not vertically aligning this opening paren,
-                       # append a character count to avoid bad alignment because
-                       # it usually looks bad to align commas within containers
-                       # for which the opening parens do not align.  Here
-                       # is an example very BAD alignment of commas (because
-                       # the atan2 functions are not all aligned):
-                       #    $XY =
-                       #      $X * $RTYSQP1 * atan2( $X, $RTYSQP1 ) +
-                       #      $Y * $RTXSQP1 * atan2( $Y, $RTXSQP1 ) -
-                       #      $X * atan2( $X,            1 ) -
-                       #      $Y * atan2( $Y,            1 );
-                       #
-                       # On the other hand, it is usually okay to align commas
-                       # if opening parens align, such as:
-                       #    glVertex3d( $cx + $s * $xs, $cy,            $z );
-                       #    glVertex3d( $cx,            $cy + $s * $ys, $z );
-                       #    glVertex3d( $cx - $s * $xs, $cy,            $z );
-                       #    glVertex3d( $cx,            $cy - $s * $ys, $z );
-                       #
-                       # To distinguish between these situations, we will append
-                       # the length of the line from the previous matching
-                       # token, or beginning of line, to the function name.
-                       # This will allow the vertical aligner to reject
-                       # undesirable matches.
+                        # Make the container name even more unique if necessary.
+                        # If we are not vertically aligning this opening paren,
+                        # append a character count to avoid bad alignment since
+                        # it usually looks bad to align commas within containers
+                        # for which the opening parens do not align.  Here
+                        # is an example very BAD alignment of commas (because
+                        # the atan2 functions are not all aligned):
+                        #    $XY =
+                        #      $X * $RTYSQP1 * atan2( $X, $RTYSQP1 ) +
+                        #      $Y * $RTXSQP1 * atan2( $Y, $RTXSQP1 ) -
+                        #      $X * atan2( $X,            1 ) -
+                        #      $Y * atan2( $Y,            1 );
+                        #
+                        # On the other hand, it is usually okay to align commas
+                        # if opening parens align, such as:
+                        #    glVertex3d( $cx + $s * $xs, $cy,            $z );
+                        #    glVertex3d( $cx,            $cy + $s * $ys, $z );
+                        #    glVertex3d( $cx - $s * $xs, $cy,            $z );
+                        #    glVertex3d( $cx,            $cy - $s * $ys, $z );
+                        #
+                        # To distinguish between these situations, we append
+                        # the length of the line from the previous matching
+                        # token, or beginning of line, to the function name.
+                        # This will allow the vertical aligner to reject
+                        # undesirable matches.
 
                         # if we are not aligning on this paren...
                         if ( !$ralignment_type_to_go->[$i] ) {
@@ -20543,9 +24056,10 @@ sub pad_token {
 
                             if ( $i_start == $ibeg ) {
 
-                              # For first token, use distance from start of line
-                              # but subtract off the indentation due to level.
-                              # Otherwise, results could vary with indentation.
+                                # For first token, use distance from start of
+                                # line but subtract off the indentation due to
+                                # level.  Otherwise, results could vary with
+                                # indentation.
                                 $len +=
                                   leading_spaces_to_go($ibeg) -
                                   $levels_to_go[$i_start] *
@@ -20556,13 +24070,14 @@ sub pad_token {
                             # tack this length onto the container name to try
                             # to make a unique token name
                             $container_name{$depth} .= "-" . $len;
-                        }
-                    }
-                }
+                        } ## end if ( !$ralignment_type_to_go...)
+                    } ## end if ( $i_mate > $i && $i_mate...)
+                } ## end if ( $is_opening_token...)
+
                 elsif ( $is_closing_type{$token} ) {
                     $depth-- if $depth > 0;
                 }
-            }
+            } ## end if ( $type_sequence_to_go...)
 
             # if we find a new synchronization token, we are done with
             # a field
@@ -20666,7 +24181,7 @@ sub pad_token {
                 $i_start = $i;
                 $j++;
                 $patterns[$j] = "";
-            }
+            } ## end if ( new synchronization token
 
             # continue accumulating tokens
 
@@ -20686,15 +24201,15 @@ sub pad_token {
                 $patterns[$j] .= $type;
             }
 
-            # handle non-keywords..
-            else {
+            # Mark most things before arrows as a quote to
+            # get them to line up. Testfile: mixed.pl.
+
+            # handle $type =~ /^[wnC]$/
+            elsif ( $is_w_n_C{$type} ) {
 
                 my $type_fix = $type;
 
-                # Mark most things before arrows as a quote to
-                # get them to line up. Testfile: mixed.pl.
-                #                      $type =~ /^[wnC]$/
-                if ( $i < $iend - 1 && $is_w_n_C{$type} ) {
+                if ( $i < $iend - 1 ) {
                     my $next_type = $types_to_go[ $i + 1 ];
                     my $i_next_nonblank =
                       ( ( $next_type eq 'b' ) ? $i + 2 : $i + 1 );
@@ -20711,8 +24226,9 @@ sub pad_token {
                     }
                 }
 
-                # Convert a bareword within braces into a quote for matching.
-                # This will allow alignment of expressions like this:
+                # Convert a bareword within braces into a quote for
+                # matching.  This will allow alignment of expressions like
+                # this:
                 #    local ( $SIG{'INT'} ) = IGNORE;
                 #    local ( $SIG{ALRM} )  = 'POSTMAN';
                 if (   $type eq 'w'
@@ -20727,26 +24243,30 @@ sub pad_token {
                 # patch to make numbers and quotes align
                 if ( $type eq 'n' ) { $type_fix = 'Q' }
 
-                # patch to ignore any ! in patterns
-                if ( $type eq '!' ) { $type_fix = '' }
-
                 $patterns[$j] .= $type_fix;
+            } ## end elsif ( $is_w_n_C{$type} )
 
-                # remove any zero-level name at first fat comma
-                if ( $depth == 0 && $type eq '=>' ) {
-                    $container_name{$depth} = "";
-                }
+            # ignore any ! in patterns
+            elsif ( $type eq '!' ) { }
 
+            # everything else
+            else {
+                $patterns[$j] .= $type;
             }
-        }
+
+            # remove any zero-level name at first fat comma
+            if ( $depth == 0 && $type eq '=>' ) {
+                $container_name{$depth} = "";
+            }
+        } ## end for my $i ( $ibeg .. $iend)
 
         # done with this line .. join text of tokens to make the last field
         push( @fields, join( '', @tokens_to_go[ $i_start .. $iend ] ) );
         push @field_lengths,
           $summed_lengths_to_go[ $iend + 1 ] - $summed_lengths_to_go[$i_start];
 
-        return ( \@tokens, \@fields, \@patterns, \@field_lengths );
-    }
+        return [ \@tokens, \@fields, \@patterns, \@field_lengths ];
+    } ## end sub make_alignment_patterns
 
 } ## end closure make_alignment_patterns
 
@@ -20780,25 +24300,27 @@ sub make_paren_name {
     return $name;
 }
 
-{    ## begin closure set_adjusted_indentation
+{    ## begin closure final_indentation_adjustment
 
     my ( $last_indentation_written, $last_unadjusted_indentation,
         $last_leading_token );
 
-    sub initialize_adjusted_indentation {
+    sub initialize_final_indentation_adjustment {
         $last_indentation_written    = 0;
         $last_unadjusted_indentation = 0;
         $last_leading_token          = "";
         return;
     }
 
-    sub set_adjusted_indentation {
+    sub final_indentation_adjustment {
 
-        # This routine has the final say regarding the actual indentation of
-        # a line.  It starts with the basic indentation which has been
-        # defined for the leading token, and then takes into account any
-        # options that the user has set regarding special indenting and
-        # outdenting.
+        #--------------------------------------------------------------------
+        # This routine sets the final indentation of a line in the Formatter.
+        #--------------------------------------------------------------------
+
+        # It starts with the basic indentation which has been defined for the
+        # leading token, and then takes into account any options that the user
+        # has set regarding special indenting and outdenting.
 
         # This routine has to resolve a number of complex interacting issues,
         # including:
@@ -20827,25 +24349,37 @@ sub make_paren_name {
         ) = @_;
 
         my $rLL                      = $self->[_rLL_];
+        my $Klimit                   = $self->[_Klimit_];
         my $ris_bli_container        = $self->[_ris_bli_container_];
         my $rseqno_controlling_my_ci = $self->[_rseqno_controlling_my_ci_];
         my $rwant_reduced_ci         = $self->[_rwant_reduced_ci_];
         my $rK_weld_left             = $self->[_rK_weld_left_];
 
-        # we need to know the last token of this line
-        my ( $terminal_type, $i_terminal ) = terminal_type_i( $ibeg, $iend );
+        # Find the last code token of this line
+        my $i_terminal    = $iend;
+        my $terminal_type = $types_to_go[$iend];
+        if ( $terminal_type eq '#' && $i_terminal > $ibeg ) {
+            $i_terminal -= 1;
+            $terminal_type = $types_to_go[$i_terminal];
+            if ( $terminal_type eq 'b' && $i_terminal > $ibeg ) {
+                $i_terminal -= 1;
+                $terminal_type = $types_to_go[$i_terminal];
+            }
+        }
 
         my $terminal_block_type = $block_type_to_go[$i_terminal];
         my $is_outdented_line   = 0;
 
-        my $terminal_is_in_list = $self->is_in_list_by_i($i_terminal);
-
-        my $type_beg      = $types_to_go[$ibeg];
-        my $token_beg     = $tokens_to_go[$ibeg];
-        my $K_beg         = $K_to_go[$ibeg];
-        my $ibeg_weld_fix = $ibeg;
-        my $seqno_beg     = $type_sequence_to_go[$ibeg];
-        my $is_bli_beg    = $seqno_beg ? $ris_bli_container->{$seqno_beg} : 0;
+        my $type_beg            = $types_to_go[$ibeg];
+        my $token_beg           = $tokens_to_go[$ibeg];
+        my $block_type_beg      = $block_type_to_go[$ibeg];
+        my $level_beg           = $levels_to_go[$ibeg];
+        my $leading_spaces_beg  = $leading_spaces_to_go[$ibeg];
+        my $K_beg               = $K_to_go[$ibeg];
+        my $seqno_beg           = $type_sequence_to_go[$ibeg];
+        my $ibeg_weld_fix       = $ibeg;
+        my $is_closing_type_beg = $is_closing_type{$type_beg};
+        my $is_bli_beg = $seqno_beg ? $ris_bli_container->{$seqno_beg} : 0;
 
         # QW INDENTATION PATCH 3:
         my $seqno_qw_closing;
@@ -20875,8 +24409,8 @@ sub make_paren_name {
 
         # MOJO: Set a flag if this lines begins with ')->'
         my $leading_paren_arrow = (
-                 $types_to_go[$ibeg] eq '}'
-              && $tokens_to_go[$ibeg] eq ')'
+                 $is_closing_type_beg
+              && $token_beg eq ')'
               && (
                 ( $ibeg < $i_terminal && $types_to_go[ $ibeg + 1 ] eq '->' )
                 || (   $ibeg < $i_terminal - 1
@@ -20885,7 +24419,7 @@ sub make_paren_name {
               )
         );
 
-        ##########################################################
+        #---------------------------------------------------------
         # Section 1: set a flag and a default indentation
         #
         # Most lines are indented according to the initial token.
@@ -20896,7 +24430,7 @@ sub make_paren_name {
         #       1 - outdent
         #       2 - vertically align with opening token
         #       3 - indent
-        ##########################################################
+        #---------------------------------------------------------
         my $adjust_indentation         = 0;
         my $default_adjust_indentation = $adjust_indentation;
 
@@ -20944,19 +24478,17 @@ sub make_paren_name {
         # that with -lp type formatting the opening and closing tokens to not
         # have sequence numbers.
         if ( $seqno_qw_closing && $total_weld_count ) {
-            my $K_next_nonblank = $self->K_next_code($K_beg);
-            if (   defined($K_next_nonblank)
-                && defined( $rK_weld_left->{$K_next_nonblank} ) )
-            {
-                my $itest = $ibeg + ( $K_next_nonblank - $K_beg );
-                if ( $itest <= $max_index_to_go ) {
-                    $ibeg_weld_fix = $itest;
+            my $i_plus = $inext_to_go[$ibeg];
+            if ( $i_plus <= $max_index_to_go ) {
+                my $K_plus = $K_to_go[$i_plus];
+                if ( defined( $rK_weld_left->{$K_plus} ) ) {
+                    $ibeg_weld_fix = $i_plus;
                 }
             }
         }
 
         # if we are at a closing token of some type..
-        if ( $is_closing_type{$type_beg} || $seqno_qw_closing ) {
+        if ( $is_closing_type_beg || $seqno_qw_closing ) {
 
             # get the indentation of the line containing the corresponding
             # opening token
@@ -20967,6 +24499,8 @@ sub make_paren_name {
               = $self->get_opening_indentation( $ibeg_weld_fix, $ri_first,
                 $ri_last, $rindentation_list, $seqno_qw_closing );
 
+            my $terminal_is_in_list = $self->is_in_list_by_i($i_terminal);
+
             # First set the default behavior:
             if (
 
@@ -20985,14 +24519,14 @@ sub make_paren_name {
                 )
 
                 # remove continuation indentation for any line like
-                #      } ... {
+                #       } ... {
                 # or without ending '{' and unbalanced, such as
                 #       such as '}->{$operator}'
                 || (
                     $type_beg eq '}'
 
                     && (   $types_to_go[$iend] eq '{'
-                        || $levels_to_go[$iend] < $levels_to_go[$ibeg] )
+                        || $levels_to_go[$iend] < $level_beg )
                 )
 
                 # and when the next line is at a lower indentation level...
@@ -21041,15 +24575,39 @@ sub make_paren_name {
             # it is the last token before a level decrease.  This will allow
             # a closing token to line up with its opening counterpart, and
             # avoids an indentation jump larger than 1 level.
-            if (   $types_to_go[$i_terminal] =~ /^[\}\]\)R]$/
-                && $i_terminal == $ibeg
-                && defined($K_beg) )
+            if (   $i_terminal == $ibeg
+                && $is_closing_type_beg
+                && defined($K_beg)
+                && $K_beg < $Klimit )
             {
-                my $K_next_nonblank = $self->K_next_code($K_beg);
+                my $K_plus    = $K_beg + 1;
+                my $type_plus = $rLL->[$K_plus]->[_TYPE_];
+
+                if ( $type_plus eq 'b' && $K_plus < $Klimit ) {
+                    $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_];
+                }
+
+                if ( $type_plus eq '#' && $K_plus < $Klimit ) {
+                    $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_];
+                    if ( $type_plus eq 'b' && $K_plus < $Klimit ) {
+                        $type_plus = $rLL->[ ++$K_plus ]->[_TYPE_];
+                    }
 
-                if ( !$is_bli_beg && defined($K_next_nonblank) ) {
-                    my $lev        = $rLL->[$K_beg]->[_LEVEL_];
-                    my $level_next = $rLL->[$K_next_nonblank]->[_LEVEL_];
+                    # Note: we have skipped past just one comment (perhaps a
+                    # side comment).  There could be more, and we could easily
+                    # skip past all the rest with the following code, or with a
+                    # while loop.  It would be rare to have to do this, and
+                    # those block comments would still be indented, so it would
+                    # to leave them indented.  So it seems best to just stop at
+                    # a maximum of one comment.
+                    ##if ($type_plus eq '#') {
+                    ##   $K_plus = $self->K_next_code($K_plus);
+                    ##}
+                }
+
+                if ( !$is_bli_beg && defined($K_plus) ) {
+                    my $lev        = $level_beg;
+                    my $level_next = $rLL->[$K_plus]->[_LEVEL_];
 
                     # and do not undo ci if it was set by the -xci option
                     $adjust_indentation = 1
@@ -21063,9 +24621,10 @@ sub make_paren_name {
                 # but right now we do not have that information.  For now
                 # we see if we are in a list, and this works well.
                 # See test files 'sub*.t' for good test cases.
-                if (   $block_type_to_go[$ibeg] =~ /$ASUB_PATTERN/
-                    && $terminal_is_in_list
-                    && !$rOpts->{'indent-closing-brace'} )
+                if (   $terminal_is_in_list
+                    && !$rOpts_indent_closing_brace
+                    && $block_type_beg
+                    && $block_type_beg =~ /$ASUB_PATTERN/ )
                 {
                     (
                         $opening_indentation, $opening_offset,
@@ -21073,7 +24632,7 @@ sub make_paren_name {
                       )
                       = $self->get_opening_indentation( $ibeg, $ri_first,
                         $ri_last, $rindentation_list );
-                    my $indentation = $leading_spaces_to_go[$ibeg];
+                    my $indentation = $leading_spaces_beg;
                     if ( defined($opening_indentation)
                         && get_spaces($indentation) >
                         get_spaces($opening_indentation) )
@@ -21087,9 +24646,12 @@ sub make_paren_name {
             # Undo ci of line with leading closing eval brace,
             # but not beyond the indention of the line with
             # the opening brace.
-            if (   $block_type_to_go[$ibeg] eq 'eval'
-                && !$rOpts->{'line-up-parentheses'}
-                && !$rOpts->{'indent-closing-brace'} )
+            if (
+                $block_type_beg eq 'eval'
+                ##&& !$rOpts_line_up_parentheses
+                && !ref($leading_spaces_beg)
+                && !$rOpts_indent_closing_brace
+              )
             {
                 (
                     $opening_indentation, $opening_offset,
@@ -21097,7 +24659,7 @@ sub make_paren_name {
                   )
                   = $self->get_opening_indentation( $ibeg, $ri_first, $ri_last,
                     $rindentation_list );
-                my $indentation = $leading_spaces_to_go[$ibeg];
+                my $indentation = $leading_spaces_beg;
                 if ( defined($opening_indentation)
                     && get_spaces($indentation) >
                     get_spaces($opening_indentation) )
@@ -21114,11 +24676,11 @@ sub make_paren_name {
             # Now modify default behavior according to user request:
             # handle option to indent non-blocks of the form );  };  ];
             # But don't do special indentation to something like ')->pack('
-            if ( !$block_type_to_go[$ibeg] ) {
+            if ( !$block_type_beg ) {
 
                 # Note that logical padding has already been applied, so we may
                 # need to remove some spaces to get a valid hash key.
-                my $tok = $tokens_to_go[$ibeg];
+                my $tok = $token_beg;
                 my $cti = $closing_token_indentation{$tok};
 
                 # Fix the value of 'cti' for an isloated non-welded closing qw
@@ -21193,7 +24755,7 @@ sub make_paren_name {
             # handle option to indent blocks
             else {
                 if (
-                    $rOpts->{'indent-closing-brace'}
+                    $rOpts_indent_closing_brace
                     && (
                         $i_terminal == $ibeg    #  isolated terminal '}'
                         || $is_semicolon_terminated
@@ -21206,8 +24768,12 @@ sub make_paren_name {
         }
 
         # if at ');', '};', '>;', and '];' of a terminal qw quote
-        elsif ($rpatterns->[0] =~ /^qb*;$/
-            && $rfields->[0] =~ /^([\)\}\]\>]);$/ )
+        elsif (
+               substr( $rpatterns->[0], 0, 2 ) eq 'qb'
+            && substr( $rfields->[0], -1, 1 ) eq ';'
+            ##&& $rpatterns->[0] =~ /^qb*;$/
+            && $rfields->[0] =~ /^([\)\}\]\>]);$/
+          )
         {
             if ( $closing_token_indentation{$1} == 0 ) {
                 $adjust_indentation = 1;
@@ -21219,7 +24785,7 @@ sub make_paren_name {
 
         # if line begins with a ':', align it with any
         # previous line leading with corresponding ?
-        elsif ( $types_to_go[$ibeg] eq ':' ) {
+        elsif ( $type_beg eq ':' ) {
             (
                 $opening_indentation, $opening_offset,
                 $is_leading,          $opening_exists
@@ -21229,7 +24795,7 @@ sub make_paren_name {
             if ($is_leading) { $adjust_indentation = 2; }
         }
 
-        ##########################################################
+        #---------------------------------------------------------
         # Section 2: set indentation according to flag set above
         #
         # Select the indentation object to define leading
@@ -21237,14 +24803,14 @@ sub make_paren_name {
         # then we want to use one level below the last token
         # ($i_terminal) in order to get it to fully outdent through
         # all levels.
-        ##########################################################
+        #---------------------------------------------------------
         my $indentation;
         my $lev;
         my $level_end = $levels_to_go[$iend];
 
         if ( $adjust_indentation == 0 ) {
-            $indentation = $leading_spaces_to_go[$ibeg];
-            $lev         = $levels_to_go[$ibeg];
+            $indentation = $leading_spaces_beg;
+            $lev         = $level_beg;
         }
         elsif ( $adjust_indentation == 1 ) {
 
@@ -21279,7 +24845,7 @@ sub make_paren_name {
         elsif ( $adjust_indentation == 2 ) {
 
             # handle option to align closing token with opening token
-            $lev = $levels_to_go[$ibeg];
+            $lev = $level_beg;
 
             # calculate spaces needed to align with opening token
             my $space_count =
@@ -21300,30 +24866,44 @@ sub make_paren_name {
             # indented, but this is better than frequently leaving it not
             # indented enough.
             my $last_spaces = get_spaces($last_indentation_written);
-            if ( !$is_closing_token{$last_leading_token} ) {
+
+            if ( ref($last_indentation_written)
+                && !$is_closing_token{$last_leading_token} )
+            {
                 $last_spaces +=
                   get_recoverable_spaces($last_indentation_written);
             }
 
             # reset the indentation to the new space count if it works
             # only options are all or none: nothing in-between looks good
-            $lev = $levels_to_go[$ibeg];
-            if ( $space_count < $last_spaces ) {
-                if ($rOpts_line_up_parentheses) {
-                    my $lev = $levels_to_go[$ibeg];
-                    $indentation =
-                      new_lp_indentation_item( $space_count, $lev, 0, 0, 0 );
-                }
-                else {
+            $lev = $level_beg;
+
+            my $diff = $last_spaces - $space_count;
+            if ( $diff > 0 ) {
+                $indentation = $space_count;
+            }
+            else {
+
+                # We need to fix things ... but there is no good way to do it.
+                # The best solution is for the user to use a longer maximum
+                # line length.  We could get a smooth variation if we just move
+                # the paren in using
+                #    $space_count -= ( 1 - $diff );
+                # But unfortunately this can give a rather unbalanced look.
+
+                # For -xlp we currently allow a tolerance of one indentation
+                # level and then revert to a simpler default.  This will jump
+                # suddenly but keeps a balanced look.
+                if (   $rOpts_extended_line_up_parentheses
+                    && $diff >= -$rOpts_indent_columns
+                    && $space_count > $leading_spaces_beg )
+                {
                     $indentation = $space_count;
                 }
-            }
 
-            # revert to default if it doesn't work
-            else {
-                $space_count = leading_spaces_to_go($ibeg);
-                if ( $default_adjust_indentation == 0 ) {
-                    $indentation = $leading_spaces_to_go[$ibeg];
+                # Otherwise revert to defaults
+                elsif ( $default_adjust_indentation == 0 ) {
+                    $indentation = $leading_spaces_beg;
                 }
                 elsif ( $default_adjust_indentation == 1 ) {
                     $indentation = $reduced_spaces_to_go[$i_terminal];
@@ -21356,7 +24936,7 @@ sub make_paren_name {
             #                 } @files;
             #         }
             #
-            if (   $block_type_to_go[$ibeg]
+            if (   $block_type_beg
                 && $ci_levels_to_go[$i_terminal] == 0 )
             {
                 my $spaces = get_spaces( $leading_spaces_to_go[$i_terminal] );
@@ -21389,15 +24969,15 @@ sub make_paren_name {
 
             # use previous indentation but use own level
             # to cause list to be flushed properly
-            $lev = $levels_to_go[$ibeg];
+            $lev = $level_beg;
         }
 
         # remember indentation except for multi-line quotes, which get
         # no indentation
         unless ( $ibeg == 0 && $starting_in_quote ) {
             $last_indentation_written    = $indentation;
-            $last_unadjusted_indentation = $leading_spaces_to_go[$ibeg];
-            $last_leading_token          = $tokens_to_go[$ibeg];
+            $last_unadjusted_indentation = $leading_spaces_beg;
+            $last_leading_token          = $token_beg;
 
             # Patch to make a line which is the end of a qw quote work with the
             # -lp option.  Make $token_beg look like a closing token as some
@@ -21422,7 +25002,6 @@ sub make_paren_name {
             #      $seqio = $gb->get_Stream_by_batch([qw(J00522 AF303112
             #      2981014)])
             #  ));
-            ## if ($seqno_qw_closing) { $last_leading_token = ')' }
             if ( $seqno_qw_closing
                 && ( length($token_beg) > 1 || $token_beg eq '>' ) )
             {
@@ -21433,22 +25012,19 @@ sub make_paren_name {
         # be sure lines with leading closing tokens are not outdented more
         # than the line which contained the corresponding opening token.
 
-        #############################################################
+        #--------------------------------------------------------
         # updated per bug report in alex_bug.pl: we must not
         # mess with the indentation of closing logical braces so
         # we must treat something like '} else {' as if it were
         # an isolated brace
-        #############################################################
-        my $is_isolated_block_brace = $block_type_to_go[$ibeg]
-          && (
-            $i_terminal == $ibeg
-            || $is_if_elsif_else_unless_while_until_for_foreach{
-                $block_type_to_go[$ibeg]
-            }
+        #--------------------------------------------------------
+        my $is_isolated_block_brace = $block_type_beg
+          && ( $i_terminal == $ibeg
+            || $is_if_elsif_else_unless_while_until_for_foreach{$block_type_beg}
           );
 
         # only do this for a ':; which is aligned with its leading '?'
-        my $is_unaligned_colon = $types_to_go[$ibeg] eq ':' && !$is_leading;
+        my $is_unaligned_colon = $type_beg eq ':' && !$is_leading;
 
         if (
             defined($opening_indentation)
@@ -21475,22 +25051,18 @@ sub make_paren_name {
             && (
 
                 # certain leading keywords if requested
-                (
-                       $rOpts->{'outdent-keywords'}
-                    && $types_to_go[$ibeg] eq 'k'
-                    && $outdent_keyword{ $tokens_to_go[$ibeg] }
-                )
+                $rOpts_outdent_keywords
+                && $type_beg eq 'k'
+                && $outdent_keyword{$token_beg}
 
                 # or labels if requested
-                || ( $rOpts->{'outdent-labels'} && $types_to_go[$ibeg] eq 'J' )
+                || $rOpts_outdent_labels && $type_beg eq 'J'
 
                 # or static block comments if requested
-                || (   $types_to_go[$ibeg] eq '#'
-                    && $rOpts->{'outdent-static-block-comments'}
-                    && $is_static_block_comment )
+                || $is_static_block_comment
+                && $rOpts_outdent_static_block_comments
             )
           )
-
         {
             my $space_count = leading_spaces_to_go($ibeg);
             if ( $space_count > 0 ) {
@@ -21501,17 +25073,11 @@ sub make_paren_name {
                 # do not promote a spaced static block comment to non-spaced;
                 # this is not normally necessary but could be for some
                 # unusual user inputs (such as -ci = -i)
-                if ( $types_to_go[$ibeg] eq '#' && $space_count == 0 ) {
+                if ( $type_beg eq '#' && $space_count == 0 ) {
                     $space_count = 1;
                 }
 
-                if ($rOpts_line_up_parentheses) {
-                    $indentation =
-                      new_lp_indentation_item( $space_count, $lev, 0, 0, 0 );
-                }
-                else {
-                    $indentation = $space_count;
-                }
+                $indentation = $space_count;
             }
         }
 
@@ -21519,7 +25085,7 @@ sub make_paren_name {
             $terminal_block_type, $is_semicolon_terminated,
             $is_outdented_line );
     }
-} ## end closure set_adjusted_indentation
+} ## end closure final_indentation_adjustment
 
 sub get_opening_indentation {
 
@@ -21575,31 +25141,55 @@ sub set_vertical_tightness_flags {
       = @_;
 
     # Define vertical tightness controls for the nth line of a batch.
-    # We create an array of parameters which tell the vertical aligner
+
+    # These parameters are passed to the vertical aligner to indicated
     # if we should combine this line with the next line to achieve the
-    # desired vertical tightness.  The array of parameters contains:
-    #
-    #   [0] type: 1=opening non-block    2=closing non-block
-    #             3=opening block brace  4=closing block brace
+    # desired vertical tightness.  This was previously an array but
+    # has been converted to a hash:
+
+    # old   hash              Meaning
+    # index key
     #
-    #   [1] flag: if opening: 1=no multiple steps, 2=multiple steps ok
-    #             if closing: spaces of padding to use
-    #   [2] sequence number of container
-    #   [3] valid flag: do not append if this flag is false. Will be
-    #       true if appropriate -vt flag is set.  Otherwise, Will be
-    #       made true only for 2 line container in parens with -lp
+    # 0   _vt_type:           1=opening non-block    2=closing non-block
+    #                         3=opening block brace  4=closing block brace
     #
-    # These flags are used by sub set_leading_whitespace in
-    # the vertical aligner
-
-    my $rvertical_tightness_flags = [ 0, 0, 0, 0, 0, 0 ];
+    # 1a  _vt_opening_flag:   1=no multiple steps, 2=multiple steps ok
+    # 1b  _vt_closing_flag:   spaces of padding to use if closing
+    # 2   _vt_seqno:          sequence number of container
+    # 3   _vt_valid flag:     do not append if this flag is false. Will be
+    #           true if appropriate -vt flag is set.  Otherwise, Will be
+    #           made true only for 2 line container in parens with -lp
+    # 4   _vt_seqno_beg:      sequence number of first token of line
+    # 5   _vt_seqno_end:      sequence number of last token of line
+    # 6   _vt_min_lines:      min number of lines for joining opening cache,
+    #                           0=no constraint
+    # 7   _vt_max_lines:      max number of lines for joining opening cache,
+    #                           0=no constraint
 
     # The vertical tightness mechanism can add whitespace, so whitespace can
     # continually increase if we allowed it when the -fws flag is set.
     # See case b499 for an example.
-    return $rvertical_tightness_flags if ($rOpts_freeze_whitespace);
 
-    # Uses these parameters:
+    # Speedup: just return for a comment
+    if ( $max_index_to_go == 0 && $types_to_go[0] eq '#' ) {
+        return;
+    }
+
+    # Define these values...
+    my $vt_type         = 0;
+    my $vt_opening_flag = 0;
+    my $vt_closing_flag = 0;
+    my $vt_seqno        = 0;
+    my $vt_valid_flag   = 0;
+    my $vt_seqno_beg    = 0;
+    my $vt_seqno_end    = 0;
+    my $vt_min_lines    = 0;
+    my $vt_max_lines    = 0;
+
+    goto RETURN
+      if ($rOpts_freeze_whitespace);
+
+    # Uses these global parameters:
     #   $rOpts_block_brace_tightness
     #   $rOpts_block_brace_vertical_tightness
     #   $rOpts_stack_closing_block_brace
@@ -21624,6 +25214,7 @@ sub set_vertical_tightness_flags {
         my $ibeg_next = $ri_first->[ $n + 1 ];
         my $token_end = $tokens_to_go[$iend];
         my $iend_next = $ri_last->[ $n + 1 ];
+
         if (
                $type_sequence_to_go[$iend]
             && !$block_type_to_go[$iend]
@@ -21634,16 +25225,32 @@ sub set_vertical_tightness_flags {
                 # allow 2-line method call to be closed up
                 || (   $rOpts_line_up_parentheses
                     && $token_end eq '('
+                    && $self->[_rlp_object_by_seqno_]
+                    ->{ $type_sequence_to_go[$iend] }
                     && $iend > $ibeg
                     && $types_to_go[ $iend - 1 ] ne 'b' )
             )
           )
         {
-
             # avoid multiple jumps in nesting depth in one line if
             # requested
             my $ovt       = $opening_vertical_tightness{$token_end};
             my $iend_next = $ri_last->[ $n + 1 ];
+
+            # Turn off the -vt flag if the next line ends in a weld.
+            # This avoids an instability with one-line welds (fixes b1183).
+            my $type_end_next = $types_to_go[$iend_next];
+            $ovt = 0
+              if ( $self->[_rK_weld_left_]->{ $K_to_go[$iend_next] }
+                && $is_closing_type{$type_end_next} );
+
+            # Avoid conflict of -bom and -pt=1 or -pt=2, fixes b1270
+            # See similar patch above for $cvt.
+            my $seqno = $type_sequence_to_go[$iend];
+            if ( $ovt && $self->[_rwant_container_open_]->{$seqno} ) {
+                $ovt = 0;
+            }
+
             unless (
                 $ovt < 2
                 && ( $nesting_depth_to_go[ $iend_next + 1 ] !=
@@ -21655,8 +25262,11 @@ sub set_vertical_tightness_flags {
                 # and aligner will validate it if it sees the closing paren
                 # within 2 lines.
                 my $valid_flag = $ovt;
-                @{$rvertical_tightness_flags} =
-                  ( 1, $ovt, $type_sequence_to_go[$iend], $valid_flag );
+
+                $vt_type         = 1;
+                $vt_opening_flag = $ovt;
+                $vt_seqno        = $type_sequence_to_go[$iend];
+                $vt_valid_flag   = $valid_flag;
             }
         }
 
@@ -21674,6 +25284,13 @@ sub set_vertical_tightness_flags {
             my $ovt = $opening_vertical_tightness{$token_next};
             my $cvt = $closing_vertical_tightness{$token_next};
 
+            # Avoid conflict of -bom and -pvt=1 or -pvt=2, fixes b977, b1303
+            # See similar patch above for $ovt.
+            my $seqno = $type_sequence_to_go[$ibeg_next];
+            if ( $cvt && $self->[_rwant_container_open_]->{$seqno} ) {
+                $cvt = 0;
+            }
+
             # Implement cvt=3: like cvt=0 for assigned structures, like cvt=1
             # otherwise.  Added for rt136417.
             if ( $cvt == 3 ) {
@@ -21681,6 +25298,15 @@ sub set_vertical_tightness_flags {
                 $cvt = $self->[_ris_assigned_structure_]->{$seqno} ? 0 : 1;
             }
 
+            # The unusual combination -pvtc=2 -dws -naws can be unstable.
+            # This fixes b1282, b1283.  This can be moved to set_options.
+            if (   $cvt == 2
+                && $rOpts_delete_old_whitespace
+                && !$rOpts_add_whitespace )
+            {
+                $cvt = 1;
+            }
+
             if (
 
                 # Never append a trailing line like   ')->pack(' because it
@@ -21699,7 +25325,9 @@ sub set_vertical_tightness_flags {
 
                             # allow closing up 2-line method calls
                             || (   $rOpts_line_up_parentheses
-                                && $token_next eq ')' )
+                                && $token_next eq ')'
+                                && $self->[_rlp_object_by_seqno_]
+                                ->{ $type_sequence_to_go[$ibeg_next] } )
                         )
                     )
                 )
@@ -21720,11 +25348,34 @@ sub set_vertical_tightness_flags {
 
                 if ($ok) {
                     my $valid_flag = $cvt;
-                    @{$rvertical_tightness_flags} = (
-                        2,
-                        $tightness{$token_next} == 2 ? 0 : 1,
-                        $type_sequence_to_go[$ibeg_next], $valid_flag,
-                    );
+                    my $min_lines  = 0;
+                    my $max_lines  = 0;
+
+                    # Fix for b1187 and b1188: Blinking can occur if we allow
+                    # welded tokens to re-form into one-line blocks during
+                    # vertical alignment when -lp used.  So for this case we
+                    # set the minimum number of lines to be 1 instead of 0.
+                    # The maximum should be 1 if -vtc is not used.  If -vtc is
+                    # used, we turn the valid
+                    # flag off and set the maximum to 0. This is equivalent to
+                    # using a large number.
+                    my $seqno_ibeg_next = $type_sequence_to_go[$ibeg_next];
+                    if (   $rOpts_line_up_parentheses
+                        && $total_weld_count
+                        && $self->[_rlp_object_by_seqno_]->{$seqno_ibeg_next}
+                        && $self->is_welded_at_seqno($seqno_ibeg_next) )
+                    {
+                        $min_lines  = 1;
+                        $max_lines  = $cvt ? 0 : 1;
+                        $valid_flag = 0;
+                    }
+
+                    $vt_type         = 2;
+                    $vt_closing_flag = $tightness{$token_next} == 2 ? 0 : 1;
+                    $vt_seqno        = $type_sequence_to_go[$ibeg_next];
+                    $vt_valid_flag   = $valid_flag;
+                    $vt_min_lines    = $min_lines;
+                    $vt_max_lines    = $max_lines;
                 }
             }
         }
@@ -21768,16 +25419,23 @@ sub set_vertical_tightness_flags {
             && $token_end ne '||' && $token_end ne '&&'
 
             # Keep break after '=' if -lp. Fixes b964 b1040 b1062 b1083 b1089.
-            && !( $token_end eq '=' && $rOpts_line_up_parentheses )
+            && !(
+                   $token_end eq '='
+                && $rOpts_line_up_parentheses
+                && $self->[_rlp_object_by_seqno_]
+                ->{ $type_sequence_to_go[$ibeg_next] }
+            )
 
             # looks bad if we align vertically with the wrong container
             && $tokens_to_go[$ibeg] ne $tokens_to_go[$ibeg_next]
           )
         {
-            my $valid_flag = 1;
-            my $spaces     = ( $types_to_go[ $ibeg_next - 1 ] eq 'b' ) ? 1 : 0;
-            @{$rvertical_tightness_flags} =
-              ( 2, $spaces, $type_sequence_to_go[$ibeg_next], $valid_flag, );
+            my $spaces = ( $types_to_go[ $ibeg_next - 1 ] eq 'b' ) ? 1 : 0;
+
+            $vt_type         = 2;
+            $vt_closing_flag = $spaces;
+            $vt_seqno        = $type_sequence_to_go[$ibeg_next];
+            $vt_valid_flag   = 1;
         }
 
         #--------------------------------------------------------------
@@ -21798,9 +25456,12 @@ sub set_vertical_tightness_flags {
         if (   $is_closing_token{$token_end}
             && $is_closing_token{$token_beg_next} )
         {
+
+            # avoid instability of combo -bom and -sct; b1179
+            my $seq_next = $type_sequence_to_go[$ibeg_next];
             $stackable = $stack_closing_token{$token_beg_next}
-              unless ( $block_type_to_go[$ibeg_next] )
-              ;    # shouldn't happen; just checking
+              unless ( $block_type_to_go[$ibeg_next]
+                || $seq_next && $self->[_rwant_container_open_]->{$seq_next} );
         }
         elsif ($is_opening_token{$token_end}
             && $is_opening_token{$token_beg_next} )
@@ -21830,11 +25491,13 @@ sub set_vertical_tightness_flags {
                     && $types_to_go[$iend_next] eq '#' )
               )
             {
-                my $valid_flag = 1;
                 my $spaces = ( $types_to_go[ $ibeg_next - 1 ] eq 'b' ) ? 1 : 0;
-                @{$rvertical_tightness_flags} = (
-                    2, $spaces, $type_sequence_to_go[$ibeg_next], $valid_flag,
-                );
+
+                $vt_type         = 2;
+                $vt_closing_flag = $spaces;
+                $vt_seqno        = $type_sequence_to_go[$ibeg_next];
+                $vt_valid_flag   = 1;
+
             }
         }
     }
@@ -21850,8 +25513,10 @@ sub set_vertical_tightness_flags {
         && $block_type_to_go[$iend] =~
         /$block_brace_vertical_tightness_pattern/ )
     {
-        @{$rvertical_tightness_flags} =
-          ( 3, $rOpts_block_brace_vertical_tightness, 0, 1 );
+        $vt_type         = 3;
+        $vt_opening_flag = $rOpts_block_brace_vertical_tightness;
+        $vt_seqno        = 0;
+        $vt_valid_flag   = 1;
     }
 
     #--------------------------------------------------------------
@@ -21870,22 +25535,40 @@ sub set_vertical_tightness_flags {
         && ( !$closing_side_comment || $n < $n_last_line ) )
     {
         my $spaces = $rOpts_block_brace_tightness == 2 ? 0 : 1;
-        @{$rvertical_tightness_flags} =
-          ( 4, $spaces, $type_sequence_to_go[$iend], 1 );
+
+        $vt_type         = 4;
+        $vt_closing_flag = $spaces;
+        $vt_seqno        = $type_sequence_to_go[$iend];
+        $vt_valid_flag   = 1;
+
     }
 
-    # pack in the sequence numbers of the ends of this line
-    my $seqno_beg = $type_sequence_to_go[$ibeg];
-    if ( !$seqno_beg && $types_to_go[$ibeg] eq 'q' ) {
-        $seqno_beg = $self->get_seqno( $ibeg, $ending_in_quote );
+    # get the sequence numbers of the ends of this line
+    $vt_seqno_beg = $type_sequence_to_go[$ibeg];
+    if ( !$vt_seqno_beg && $types_to_go[$ibeg] eq 'q' ) {
+        $vt_seqno_beg = $self->get_seqno( $ibeg, $ending_in_quote );
     }
-    my $seqno_end = $type_sequence_to_go[$iend];
-    if ( !$seqno_end && $types_to_go[$iend] eq 'q' ) {
-        $seqno_end = $self->get_seqno( $iend, $ending_in_quote );
+
+    $vt_seqno_end = $type_sequence_to_go[$iend];
+    if ( !$vt_seqno_end && $types_to_go[$iend] eq 'q' ) {
+        $vt_seqno_end = $self->get_seqno( $iend, $ending_in_quote );
     }
-    $rvertical_tightness_flags->[4] = $seqno_beg;
-    $rvertical_tightness_flags->[5] = $seqno_end;
-    return $rvertical_tightness_flags;
+
+  RETURN:
+
+    my $rvertical_tightness_flags = {
+        _vt_type         => $vt_type,
+        _vt_opening_flag => $vt_opening_flag,
+        _vt_closing_flag => $vt_closing_flag,
+        _vt_seqno        => $vt_seqno,
+        _vt_valid_flag   => $vt_valid_flag,
+        _vt_seqno_beg    => $vt_seqno_beg,
+        _vt_seqno_end    => $vt_seqno_end,
+        _vt_min_lines    => $vt_min_lines,
+        _vt_max_lines    => $vt_max_lines,
+    };
+
+    return ($rvertical_tightness_flags);
 }
 
 ##########################################################
@@ -22339,8 +26022,8 @@ sub set_vertical_tightness_flags {
 
 sub add_closing_side_comment {
 
-    my $self = shift;
-    my $rLL  = $self->[_rLL_];
+    my ( $self, $ri_first, $ri_last ) = @_;
+    my $rLL = $self->[_rLL_];
 
     # add closing side comments after closing block braces if -csc used
     my ( $closing_side_comment, $cscw_block_comment );
@@ -22364,6 +26047,9 @@ sub add_closing_side_comment {
     if (
         $terminal_type eq '}'
 
+        # Fix 1 for c091, this is only for blocks
+        && $block_type_to_go[$i_terminal]
+
         # ..and either
         && (
 
@@ -22499,22 +26185,45 @@ sub add_closing_side_comment {
                     if ( $block_line_count <
                         $rOpts->{'closing-side-comment-interval'} )
                     {
+                        # Since the line breaks have already been set, we have
+                        # to remove the token from the _to_go array and also
+                        # from the line range (this fixes issue c081).
+                        # Note that we can only get here if -cscw has been set
+                        # because otherwise the old comment is already deleted.
                         $token = undef;
-                        $self->unstore_token_to_go()
-                          if ( $types_to_go[$max_index_to_go] eq '#' );
-                        $self->unstore_token_to_go()
-                          if ( $types_to_go[$max_index_to_go] eq 'b' );
+                        my $ibeg = $ri_first->[-1];
+                        my $iend = $ri_last->[-1];
+                        if (   $iend > $ibeg
+                            && $iend == $max_index_to_go
+                            && $types_to_go[$max_index_to_go] eq '#' )
+                        {
+                            $iend--;
+                            $max_index_to_go--;
+                            if (   $iend > $ibeg
+                                && $types_to_go[$max_index_to_go] eq 'b' )
+                            {
+                                $iend--;
+                                $max_index_to_go--;
+                            }
+                            $ri_last->[-1] = $iend;
+                        }
                     }
                 }
             }
 
             # switch to the new csc (unless we deleted it!)
             if ($token) {
-                $tokens_to_go[$max_index_to_go] = $token;
+
+                my $len_tok = length($token); # NOTE: length no longer important
+                my $added_len =
+                  $len_tok - $token_lengths_to_go[$max_index_to_go];
+
+                $tokens_to_go[$max_index_to_go]        = $token;
+                $token_lengths_to_go[$max_index_to_go] = $len_tok;
                 my $K = $K_to_go[$max_index_to_go];
-                $rLL->[$K]->[_TOKEN_] = $token;
-                $rLL->[$K]->[_TOKEN_LENGTH_] =
-                  length($token);    # NOTE: length no longer important
+                $rLL->[$K]->[_TOKEN_]        = $token;
+                $rLL->[$K]->[_TOKEN_LENGTH_] = $len_tok;
+                $summed_lengths_to_go[ $max_index_to_go + 1 ] += $added_len;
             }
         }
 
@@ -22546,6 +26255,13 @@ sub wrapup {
     $file_writer_object->decrement_output_line_number()
       ;    # fix up line number since it was incremented
     we_are_at_the_last_line();
+
+    my $max_depth = $self->[_maximum_BLOCK_level_];
+    my $at_line   = $self->[_maximum_BLOCK_level_at_line_];
+    write_logfile_entry(
+"Maximum leading structural depth is $max_depth in input at line $at_line\n"
+    );
+
     my $added_semicolon_count    = $self->[_added_semicolon_count_];
     my $first_added_semicolon_at = $self->[_first_added_semicolon_at_];
     my $last_added_semicolon_at  = $self->[_last_added_semicolon_at_];
index 8c11ee5afba7c6a6078d19a1395d9216f5caa0c5..eba6335a47cfead7ab7a0090c1880a508f737689 100644 (file)
@@ -7,7 +7,7 @@
 package Perl::Tidy::HtmlWriter;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 use File::Basename;
 
@@ -226,6 +226,7 @@ sub add_toc_item {
             $html_toc_fh->print("</ul>\n</li>\n");
             ${$rin_toc_package} = "";
         }
+        return;
     };
 
     my $start_package_list = sub {
@@ -236,6 +237,7 @@ sub add_toc_item {
 <ul>
 EOM
         ${$rin_toc_package} = $package;
+        return;
     };
 
     # start the table of contents on the first item
@@ -763,6 +765,7 @@ sub pod_to_html {
             $html_fh->print($_) unless ($no_print);
             if ($in_toc) { push @toc, $_ }
         }
+        return;
     };
 
     # loop over lines of html output from pod2html and merge in
@@ -1429,6 +1432,8 @@ sub write_line {
         elsif ( $line_type eq 'HERE_END' )   { $line_character = 'h' }
         elsif ( $line_type eq 'FORMAT' )     { $line_character = 'H' }
         elsif ( $line_type eq 'FORMAT_END' ) { $line_character = 'h' }
+        elsif ( $line_type eq 'SKIP' )       { $line_character = 'H' }
+        elsif ( $line_type eq 'SKIP_END' )   { $line_character = 'h' }
         elsif ( $line_type eq 'SYSTEM' )     { $line_character = 'c' }
         elsif ( $line_type eq 'END_START' ) {
             $line_character = 'k';
@@ -1503,4 +1508,3 @@ EOM
     return;
 }
 1;
-
index 1a96c54ae6c2ef1f8c919cffc1b1ca7c3e9ea9b6..49151db192b45cfa4d95606f290fa14e4e5345f9 100644 (file)
@@ -10,7 +10,7 @@ package Perl::Tidy::IOScalar;
 use strict;
 use warnings;
 use Carp;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 sub AUTOLOAD {
 
index 640459b85134fc83445297c702886bc608591ef5..1c958060fa244134b17c6c3e4ba80d7f005c3639 100644 (file)
@@ -14,7 +14,7 @@ package Perl::Tidy::IOScalarArray;
 use strict;
 use warnings;
 use Carp;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 sub AUTOLOAD {
 
index 14e00719a8940124d65cc1355d4f3e5b9d338e39..9244a03c8f78da779f8fe58192fb7f3fdd73a279 100644 (file)
@@ -8,11 +8,12 @@
 package Perl::Tidy::IndentationItem;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 BEGIN {
 
     # Array index names
+    # Do not combine with other BEGIN blocks (c101).
     my $i = 0;
     use constant {
         _spaces_             => $i++,
@@ -21,14 +22,13 @@ BEGIN {
         _available_spaces_   => $i++,
         _closed_             => $i++,
         _comma_count_        => $i++,
-        _sequence_number_    => $i++,
-        _index_              => $i++,
+        _lp_item_index_      => $i++,
         _have_child_         => $i++,
         _recoverable_spaces_ => $i++,
-        _align_paren_        => $i++,
+        _align_seqno_        => $i++,
         _marked_             => $i++,
         _stack_depth_        => $i++,
-        _starting_index_K_   => $i++,
+        _K_begin_line_       => $i++,
         _arrow_count_        => $i++,
     };
 }
@@ -73,17 +73,16 @@ sub new {
     #                        # for this level
     # closed             =>  # index where we saw closing '}'
     # comma_count        =>  # how many commas at this level?
-    # sequence_number    =>  # output batch number
-    # index              =>  # index in output batch list
+    # lp_item_index     =>  # index in output batch list
     # have_child         =>  # any dependents?
     # recoverable_spaces =>  # how many spaces to the right
     #                        # we would like to move to get
     #                        # alignment (negative if left)
-    # align_paren        =>  # do we want to try to align
-    #                        # with an opening structure?
+    # align_seqno        =>  # if we are aligning with an opening structure,
+    #                        # this is its seqno
     # marked             =>  # if visited by corrector logic
     # stack_depth        =>  # indentation nesting depth
-    # starting_index_K   =>  # first token index K of this level
+    # K_begin_line   =>  # first token index K of this level
     # arrow_count        =>  # how many =>'s
 
     my $self = [];
@@ -93,14 +92,13 @@ sub new {
     $self->[_available_spaces_]   = $input_hash{available_spaces};
     $self->[_closed_]             = -1;
     $self->[_comma_count_]        = 0;
-    $self->[_sequence_number_]    = $input_hash{gnu_sequence_number};
-    $self->[_index_]              = $input_hash{index};
+    $self->[_lp_item_index_]      = $input_hash{lp_item_index};
     $self->[_have_child_]         = 0;
     $self->[_recoverable_spaces_] = 0;
-    $self->[_align_paren_]        = $input_hash{align_paren};
+    $self->[_align_seqno_]        = $input_hash{align_seqno};
     $self->[_marked_]             = 0;
     $self->[_stack_depth_]        = $input_hash{stack_depth};
-    $self->[_starting_index_K_]   = $input_hash{starting_index_K};
+    $self->[_K_begin_line_]       = $input_hash{K_begin_line};
     $self->[_arrow_count_]        = 0;
 
     bless $self, $class;
@@ -119,7 +117,12 @@ sub permanently_decrease_available_spaces {
       ( $available_spaces > $spaces_needed )
       ? $spaces_needed
       : $available_spaces;
-    $item->decrease_available_spaces($deleted_spaces);
+
+    # Fixed for c085; a zero value must remain unchanged unless the closed
+    # flag has been set.
+    my $closed = $item->get_closed();
+    $item->decrease_available_spaces($deleted_spaces)
+      unless ( $available_spaces == 0 && $closed < 0 );
     $item->decrease_SPACES($deleted_spaces);
     $item->set_recoverable_spaces(0);
 
@@ -184,8 +187,8 @@ sub decrease_available_spaces {
     return $self->[_available_spaces_];
 }
 
-sub get_align_paren {
-    return $_[0]->[_align_paren_];
+sub get_align_seqno {
+    return $_[0]->[_align_seqno_];
 }
 
 sub get_recoverable_spaces {
@@ -216,16 +219,17 @@ sub get_level {
     return $_[0]->[_level_];
 }
 
-sub get_sequence_number {
-    return $_[0]->[_sequence_number_];
+sub get_spaces_level_ci {
+    my $self = shift;
+    return [ $self->[_spaces_], $self->[_level_], $self->[_ci_level_] ];
 }
 
-sub get_index {
-    return $_[0]->[_index_];
+sub get_lp_item_index {
+    return $_[0]->[_lp_item_index_];
 }
 
-sub get_starting_index_K {
-    return $_[0]->[_starting_index_K_];
+sub get_K_begin_line {
+    return $_[0]->[_K_begin_line_];
 }
 
 sub set_have_child {
index d77a84120c7cb5758a8586de6978ffe8fdf77bdf..0a9ccfcb4e2127498c7d60e46c317dd231e9573f 100644 (file)
@@ -12,7 +12,7 @@
 package Perl::Tidy::LineBuffer;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 sub AUTOLOAD {
 
index a3cee83ed3c2bb3bef812aec25f2734be1bac268..65da8f3b156fff8e7671e4cdc8a208efe9d2504c 100644 (file)
@@ -8,7 +8,7 @@
 package Perl::Tidy::LineSink;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 sub AUTOLOAD {
 
index f4798ca9547b13110ac91fd36d16fc7a9467cd0d..53ce46d1be0fb696f45ae1430976a19e2dd3387f 100644 (file)
@@ -8,7 +8,7 @@
 package Perl::Tidy::LineSource;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 sub AUTOLOAD {
 
index 14927e8ed57cd628adb9c4c1e092254686c77cbd..910ee49048f8b33d16835465be8aef758dc899b6 100644 (file)
@@ -7,7 +7,7 @@
 package Perl::Tidy::Logger;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 sub AUTOLOAD {
 
@@ -435,113 +435,32 @@ sub warning {
     return;
 }
 
-# programming bug codes:
-#   -1 = no bug
-#    0 = maybe, not sure.
-#    1 = definitely
-sub report_possible_bug {
-    my $self         = shift;
-    my $saw_code_bug = $self->{_saw_code_bug};
-    $self->{_saw_code_bug} = ( $saw_code_bug < 0 ) ? 0 : $saw_code_bug;
-    return;
-}
-
 sub report_definite_bug {
     my $self = shift;
     $self->{_saw_code_bug} = 1;
     return;
 }
 
-sub ask_user_for_bug_report {
-
-    my ( $self, $infile_syntax_ok, $formatter ) = @_;
-    my $saw_code_bug = $self->{_saw_code_bug};
-    if ( ( $saw_code_bug == 0 ) && ( $infile_syntax_ok == 1 ) ) {
-        $self->warning(<<EOM);
-
-You may have encountered a code bug in perltidy.  If you think so, and
-the problem is not listed in the BUGS file at
-http://perltidy.sourceforge.net, please report it so that it can be
-corrected.  Include the smallest possible script which has the problem,
-along with the .LOG file. See the manual pages for contact information.
-Thank you!
-EOM
-
-    }
-    elsif ( $saw_code_bug == 1 ) {
-        if ( $self->{_saw_extrude} ) {
-            $self->warning(<<EOM);
-
-You may have encountered a bug in perltidy.  However, since you are using the
--extrude option, the problem may be with perl or one of its modules, which have
-occasional problems with this type of file.  If you believe that the
-problem is with perltidy, and the problem is not listed in the BUGS file at
-http://perltidy.sourceforge.net, please report it so that it can be corrected.
-Include the smallest possible script which has the problem, along with the .LOG
-file. See the manual pages for contact information.
-Thank you!
-EOM
-        }
-        else {
-            $self->warning(<<EOM);
-
-Oops, you seem to have encountered a bug in perltidy.  Please check the
-BUGS file at http://perltidy.sourceforge.net.  If the problem is not
-listed there, please report it so that it can be corrected.  Include the
-smallest possible script which produces this message, along with the
-.LOG file if appropriate.  See the manual pages for contact information.
-Your efforts are appreciated.  
-Thank you!
-EOM
-            my $added_semicolon_count = 0;
-            eval {
-                $added_semicolon_count =
-                  $formatter->get_added_semicolon_count();
-            };
-            if ( $added_semicolon_count > 0 ) {
-                $self->warning(<<EOM);
-
-The log file shows that perltidy added $added_semicolon_count semicolons.
-Please rerun with -nasc to see if that is the cause of the syntax error.  Even
-if that is the problem, please report it so that it can be fixed.
-EOM
-
-            }
-        }
-    }
-    return;
-}
-
 sub get_save_logfile {
 
     # To be called after tokenizer has finished to make formatting more
-    # efficient.  This is not precisely the same as the check used below
-    # because we don't yet have the syntax check result, but since syntax
-    # checking is off by default it will be the same except in debug runs with
-    # syntax checking activated.  In that case it will tell the formatter to
-    # save the logfile even if it may actually be deleted based on the syntax
-    # check.
+    # efficient.
     my $self         = shift;
     my $saw_code_bug = $self->{_saw_code_bug};
     my $rOpts        = $self->{_rOpts};
-    return
-         $saw_code_bug == 1
-      || $rOpts->{'logfile'}
-      || $rOpts->{'check-syntax'};
+    return $saw_code_bug == 1 || $rOpts->{'logfile'};
 }
 
 sub finish {
 
     # called after all formatting to summarize errors
-    my ( $self, $infile_syntax_ok, $formatter ) = @_;
+    my ( $self, $formatter ) = @_;
 
     my $rOpts         = $self->{_rOpts};
     my $warning_count = $self->{_warning_count};
     my $saw_code_bug  = $self->{_saw_code_bug};
 
-    my $save_logfile =
-         ( $saw_code_bug == 0 && $infile_syntax_ok == 1 )
-      || $saw_code_bug == 1
+    my $save_logfile = $saw_code_bug == 1
       || $rOpts->{'logfile'};
     my $log_file = $self->{_log_file};
     if ($warning_count) {
@@ -564,7 +483,6 @@ sub finish {
             $self->warning("To save a full .LOG file rerun with -g\n");
         }
     }
-    $self->ask_user_for_bug_report( $infile_syntax_ok, $formatter );
 
     if ($save_logfile) {
         my $log_file        = $self->{_log_file};
index c012e47f9d89b0a0c068a375f21c8ece343ced89..b5305063b3cb9fa406f98614f06b342ee9eb3163 100644 (file)
 package Perl::Tidy::Tokenizer;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
+
+# this can be turned on for extra checking during development
+use constant DEVEL_MODE => 0;
 
 use Perl::Tidy::LineBuffer;
 use Carp;
@@ -55,6 +58,7 @@ use vars qw{
   @current_depth
   @total_depth
   $total_depth
+  $next_sequence_number
   @nesting_sequence_number
   @current_sequence_number
   @paren_type
@@ -88,6 +92,8 @@ use vars qw{
   %is_valid_token_type
   %is_keyword
   %is_code_block_token
+  %is_sort_map_grep_eval_do
+  %is_grep_alias
   %really_want_term
   @opening_brace_names
   @closing_brace_names
@@ -128,7 +134,8 @@ use constant MAX_NAG_MESSAGES => 6;
 
 BEGIN {
 
-    # Array index names for $self
+    # Array index names for $self.
+    # Do not combine with other BEGIN blocks (c101).
     my $i = 0;
     use constant {
         _rhere_target_list_                  => $i++,
@@ -229,6 +236,35 @@ sub Die {
     croak "unexpected return from Perl::Tidy::Die";
 }
 
+sub Fault {
+    my ($msg) = @_;
+
+    # This routine is called for errors that really should not occur
+    # except if there has been a bug introduced by a recent program change.
+    # Please add comments at calls to Fault to explain why the call
+    # should not occur, and where to look to fix it.
+    my ( $package0, $filename0, $line0, $subroutine0 ) = caller(0);
+    my ( $package1, $filename1, $line1, $subroutine1 ) = caller(1);
+    my ( $package2, $filename2, $line2, $subroutine2 ) = caller(2);
+    my $input_stream_name = get_input_stream_name();
+
+    Die(<<EOM);
+==============================================================================
+While operating on input stream with name: '$input_stream_name'
+A fault was detected at line $line0 of sub '$subroutine1'
+in file '$filename1'
+which was called from line $line1 of sub '$subroutine2'
+Message: '$msg'
+This is probably an error introduced by a recent programming change.
+Perl::Tidy::Tokenizer.pm reports VERSION='$VERSION'.
+==============================================================================
+EOM
+
+    # We shouldn't get here, but this return is to keep Perl-Critic from
+    # complaining.
+    return;
+}
+
 sub bad_pattern {
 
     # See if a pattern will compile. We have to use a string eval here,
@@ -276,6 +312,15 @@ sub check_options {
         }
     }
 
+    %is_grep_alias = ();
+    if ( $rOpts->{'grep-alias-list'} ) {
+
+        # Note that 'grep-alias-list' has been preprocessed to be a trimmed,
+        # space-separated list
+        my @q = split /\s+/, $rOpts->{'grep-alias-list'};
+        @{is_grep_alias}{@q} = (1) x scalar(@q);
+    }
+
     $rOpts_code_skipping = $rOpts->{'code-skipping'};
     $code_skipping_pattern_begin =
       make_code_skipping_pattern( $rOpts, 'code-skipping-begin', '#<<V' );
@@ -422,6 +467,15 @@ sub warning {
     return;
 }
 
+sub get_input_stream_name {
+    my $input_stream_name = "";
+    my $logger_object     = $tokenizer_self->[_logger_object_];
+    if ($logger_object) {
+        $input_stream_name = $logger_object->get_input_stream_name();
+    }
+    return $input_stream_name;
+}
+
 sub complain {
     my $msg           = shift;
     my $logger_object = $tokenizer_self->[_logger_object_];
@@ -601,9 +655,10 @@ EOM
             );
         }
         else {
-            warning(
-"hit EOF in here document starting at line $started_looking_for_here_target_at with empty target string\n"
-            );
+            warning(<<EOM);
+Hit EOF in here document starting at line $started_looking_for_here_target_at with empty target string.
+  (Perl will match to the end of file but this may not be intended).
+EOM
         }
         my $nearly_matched_here_target_at =
           $tokenizer_self->[_nearly_matched_here_target_at_];
@@ -692,6 +747,11 @@ sub report_v_string {
     return;
 }
 
+sub is_valid_token_type {
+    my ($type) = @_;
+    return $is_valid_token_type{$type};
+}
+
 sub get_input_line_number {
     return $tokenizer_self->[_last_line_number_];
 }
@@ -714,6 +774,7 @@ sub get_line {
     my $write_logfile_entry = sub {
         my ($msg) = @_;
         write_logfile_entry("Line $input_line_number: $msg");
+        return;
     };
 
     # Find and remove what characters terminate this line, including any
@@ -747,6 +808,8 @@ sub get_line {
     #   HERE_END       - last line of here-doc (target word)
     #   FORMAT         - format section
     #   FORMAT_END     - last line of format section, '.'
+    #   SKIP           - code skipping section
+    #   SKIP_END       - last line of code skipping section, '#>>V'
     #   DATA_START     - __DATA__ line
     #   DATA           - unidentified text following __DATA__
     #   END_START      - __END__ line
@@ -884,9 +947,9 @@ sub get_line {
     # print line unchanged if in skipped section
     elsif ( $tokenizer_self->[_in_skipped_] ) {
 
-        # NOTE: marked as the existing type 'FORMAT' to keep html working
-        $line_of_tokens->{_line_type} = 'FORMAT';
+        $line_of_tokens->{_line_type} = 'SKIP';
         if ( $input_line =~ /$code_skipping_pattern_end/ ) {
+            $line_of_tokens->{_line_type} = 'SKIP_END';
             $write_logfile_entry->("Exiting code-skipping section\n");
             $tokenizer_self->[_in_skipped_] = 0;
         }
@@ -1061,7 +1124,7 @@ sub get_line {
                 $line_of_tokens->{_line_type} = 'POD_START';
                 warning(
 "=cut starts a pod section .. this can fool pod utilities.\n"
-                );
+                ) unless (DEVEL_MODE);
                 $write_logfile_entry->("Entering POD section\n");
             }
         }
@@ -1077,8 +1140,7 @@ sub get_line {
     # handle start of skipped section
     if ( $tokenizer_self->[_in_skipped_] ) {
 
-        # NOTE: marked as the existing type 'FORMAT' to keep html working
-        $line_of_tokens->{_line_type} = 'FORMAT';
+        $line_of_tokens->{_line_type} = 'SKIP';
         $write_logfile_entry->("Entering code-skipping section\n");
         return $line_of_tokens;
     }
@@ -1344,6 +1406,7 @@ sub prepare_for_a_new_file {
     @total_depth             = ();
     @nesting_sequence_number = ( 0 .. @closing_brace_names - 1 );
     @current_sequence_number = ();
+    $next_sequence_number    = 2;    # The value 1 is reserved for SEQ_ROOT
 
     @paren_type                     = ();
     @paren_semicolon_count          = ();
@@ -1549,7 +1612,7 @@ sub prepare_for_a_new_file {
         (
             $routput_token_list,    $routput_token_type,
             $routput_block_type,    $routput_container_type,
-            $routput_type_sequence, $routput_type_sequence,
+            $routput_type_sequence, $routput_indent_flag,
         ) = @{$rTV2};
 
         (
@@ -1582,6 +1645,90 @@ sub prepare_for_a_new_file {
         return;
     }
 
+    sub split_pretoken {
+
+        my ($numc) = @_;
+
+     # Split the leading $numc characters from the current token (at index=$i)
+     # which is pre-type 'w' and insert the remainder back into the pretoken
+     # stream with appropriate settings.  Since we are splitting a pre-type 'w',
+     # there are three cases, depending on if the remainder starts with a digit:
+     # Case 1: remainder is type 'd', all digits
+     # Case 2: remainder is type 'd' and type 'w': digits and other characters
+     # Case 3: remainder is type 'w'
+
+        # Examples, for $numc=1:
+        #   $tok    => $tok_0 $tok_1 $tok_2
+        #   'x10'   => 'x'    '10'                # case 1
+        #   'x10if' => 'x'    '10'   'if'         # case 2
+        #   '0ne    => 'O'            'ne'        # case 3
+
+        # where:
+        #   $tok_1 is a possible string of digits (pre-type 'd')
+        #   $tok_2 is a possible word (pre-type 'w')
+
+        # return 1 if successful
+        # return undef if error (shouldn't happen)
+
+        # Calling routine should update '$type' and '$tok' if successful.
+
+        my $pretoken = $rtokens->[$i];
+        if (   $pretoken
+            && length($pretoken) > $numc
+            && substr( $pretoken, $numc ) =~ /^(\d*)(.*)$/ )
+        {
+
+            # Split $tok into up to 3 tokens:
+            my $tok_0 = substr( $pretoken, 0, $numc );
+            my $tok_1 = defined($1) ? $1 : "";
+            my $tok_2 = defined($2) ? $2 : "";
+
+            my $len_0 = length($tok_0);
+            my $len_1 = length($tok_1);
+            my $len_2 = length($tok_2);
+
+            my $pre_type_0 = 'w';
+            my $pre_type_1 = 'd';
+            my $pre_type_2 = 'w';
+
+            my $pos_0 = $rtoken_map->[$i];
+            my $pos_1 = $pos_0 + $len_0;
+            my $pos_2 = $pos_1 + $len_1;
+
+            my $isplice = $i + 1;
+
+            # Splice in any digits
+            if ($len_1) {
+                splice @{$rtoken_map},  $isplice, 0, $pos_1;
+                splice @{$rtokens},     $isplice, 0, $tok_1;
+                splice @{$rtoken_type}, $isplice, 0, $pre_type_1;
+                $max_token_index++;
+                $isplice++;
+            }
+
+            # Splice in any trailing word
+            if ($len_2) {
+                splice @{$rtoken_map},  $isplice, 0, $pos_2;
+                splice @{$rtokens},     $isplice, 0, $tok_2;
+                splice @{$rtoken_type}, $isplice, 0, $pre_type_2;
+                $max_token_index++;
+            }
+
+            $rtokens->[$i] = $tok_0;
+            return 1;
+        }
+        else {
+
+            # Shouldn't get here
+            if (DEVEL_MODE) {
+                Fault(<<EOM);
+While working near line number $input_line_number, bad arg '$tok' passed to sub split_pretoken()
+EOM
+            }
+        }
+        return;
+    }
+
     sub get_indentation_level {
 
         # patch to avoid reporting error if indented if is not terminated
@@ -1646,7 +1793,7 @@ sub prepare_for_a_new_file {
             @brace_package,                  @square_bracket_type,
             @square_bracket_structural_type, @depth_array,
             @starting_line_of_current_depth, @nested_ternary_flag,
-            @nested_statement_type,
+            @nested_statement_type,          $next_sequence_number,
         );
 
         # save all lexical variables
@@ -1709,6 +1856,33 @@ sub prepare_for_a_new_file {
         ( $i, $tok, $type, $id_scan_state, $identifier ) =
           scan_identifier_do( $i, $id_scan_state, $identifier, $rtokens,
             $max_token_index, $expecting, $paren_type[$paren_depth] );
+
+        # Check for signal to fix a special variable adjacent to a keyword,
+        # such as '$^One$0'.
+        if ( $id_scan_state eq '^' ) {
+
+            # Try to fix it by splitting the pretoken
+            if (   $i > 0
+                && $rtokens->[ $i - 1 ] eq '^'
+                && split_pretoken(1) )
+            {
+                $identifier = substr( $identifier, 0, 3 );
+                $tok        = $identifier;
+            }
+            else {
+
+                # This shouldn't happen ...
+                my $var    = substr( $tok, 0, 3 );
+                my $excess = substr( $tok, 3 );
+                interrupt_logfile();
+                warning(<<EOM);
+$input_line_number: Trouble parsing at characters '$excess' after special variable '$var'.
+A space may be needed after '$var'. 
+EOM
+                resume_logfile();
+            }
+            $id_scan_state = "";
+        }
         return;
     }
 
@@ -2073,7 +2247,12 @@ EOM
                     || $last_nonblank_type eq 'U' )    # possible object
               )
             {
-                $type = 'Z';
+
+                # An identifier followed by '->' is not indirect object;
+                # fixes b1175, b1176
+                my ( $next_nonblank_type, $i_next ) =
+                  find_next_noncomment_type( $i, $rtokens, $max_token_index );
+                $type = 'Z' if ( $next_nonblank_type ne '->' );
             }
         },
         '(' => sub {
@@ -2441,7 +2620,8 @@ EOM
                     && $last_nonblank_i >= 0 )
                 {
                     if ( $routput_token_type->[$last_nonblank_i] eq 'w' ) {
-                        $routput_token_type->[$last_nonblank_i] = 'G';
+                        $routput_token_type->[$last_nonblank_i] =
+                          $is_grep_alias{$block_type} ? 'k' : 'G';
                     }
                 }
 
@@ -2791,9 +2971,6 @@ EOM
             # check for special variables like ${^WARNING_BITS}
             if ( $expecting == TERM ) {
 
-                # FIXME: this should work but will not catch errors
-                # because we also have to be sure that previous token is
-                # a type character ($,@,%).
                 if (   $last_nonblank_token eq '{'
                     && ( $next_tok !~ /^\d/ )
                     && ( $next_tok =~ /^\w/ ) )
@@ -2805,6 +2982,24 @@ EOM
                     $tok  = $tok . $next_tok;
                     $i    = $i + 1;
                     $type = 'w';
+
+                    # Optional coding to try to catch syntax errors. This can
+                    # be removed if it ever causes incorrect warning messages.
+                    # The '{^' should be preceded by either by a type or '$#'
+                    # Examples:
+                    #   $#{^CAPTURE}       ok
+                    #   *${^LAST_FH}{NAME} ok
+                    #   @{^HOWDY}          ok
+                    #   $hash{^HOWDY}      error
+
+                    # Note that a type sigil '$' may be tokenized as 'Z'
+                    # after something like 'print', so allow type 'Z'
+                    if (   $last_last_nonblank_type ne 't'
+                        && $last_last_nonblank_type ne 'Z'
+                        && $last_last_nonblank_token ne '$#' )
+                    {
+                        warning("Possible syntax error near '{^'\n");
+                    }
                 }
 
                 else {
@@ -2860,8 +3055,16 @@ EOM
                 elsif ( $expecting == TERM ) {
                     unless ($saw_error) {
 
-                        # shouldn't happen..
-                        warning("Program bug; didn't find here doc target\n");
+                        # shouldn't happen..arriving here implies an error in
+                        # the logic in sub 'find_here_doc'
+                        if (DEVEL_MODE) {
+                            Fault(<<EOM);
+Program bug; didn't find here doc target
+EOM
+                        }
+                        warning(
+"Possible program error: didn't find here doc target\n"
+                        );
                         report_definite_bug();
                     }
                 }
@@ -2904,13 +3107,22 @@ EOM
                 elsif ( $expecting == TERM ) {
                     unless ($saw_error) {
 
-                        # shouldn't happen..
-                        warning("Program bug; didn't find here doc target\n");
+                        # shouldn't happen..arriving here implies an error in
+                        # the logic in sub 'find_here_doc'
+                        if (DEVEL_MODE) {
+                            Fault(<<EOM);
+Program bug; didn't find here doc target
+EOM
+                        }
+                        warning(
+"Possible program error: didn't find here doc target\n"
+                        );
                         report_definite_bug();
                     }
                 }
             }
             else {
+                error_if_expecting_OPERATOR();
             }
         },
         '->' => sub {
@@ -3007,10 +3219,6 @@ EOM
       if elsif else unless while until for foreach switch case given when);
     @is_zero_continuation_block_type{@_} = (1) x scalar(@_);
 
-    my %is_not_zero_continuation_block_type;
-    @_ = qw(sort grep map do eval);
-    @is_not_zero_continuation_block_type{@_} = (1) x scalar(@_);
-
     my %is_logical_container;
     @_ = qw(if elsif unless while and or err not && !  || for foreach);
     @is_logical_container{@_} = (1) x scalar(@_);
@@ -3306,7 +3514,11 @@ EOM
                     $routput_token_type->[$i] = $type;
 
                 }
-                $tok = $quote_character if ($quote_character);
+
+                # Removed to fix b1280.  This is not needed and was causing the
+                # starting type 'qw' to be lost, leading to mis-tokenization of
+                # a trailing block brace in a parenless for stmt 'for .. qw.. {'
+                ##$tok = $quote_character if ($quote_character);
 
                 # scan for the end of the quote or pattern
                 (
@@ -3435,20 +3647,27 @@ EOM
                     }
                 }
 
-                $last_last_nonblank_token      = $last_nonblank_token;
-                $last_last_nonblank_type       = $last_nonblank_type;
-                $last_last_nonblank_block_type = $last_nonblank_block_type;
-                $last_last_nonblank_container_type =
-                  $last_nonblank_container_type;
-                $last_last_nonblank_type_sequence =
-                  $last_nonblank_type_sequence;
-                $last_nonblank_token          = $tok;
-                $last_nonblank_type           = $type;
-                $last_nonblank_prototype      = $prototype;
-                $last_nonblank_block_type     = $block_type;
-                $last_nonblank_container_type = $container_type;
-                $last_nonblank_type_sequence  = $type_sequence;
-                $last_nonblank_i              = $i_tok;
+                # fix c090, only rotate vars if a new token will be stored
+                if ( $i_tok >= 0 ) {
+                    $last_last_nonblank_token      = $last_nonblank_token;
+                    $last_last_nonblank_type       = $last_nonblank_type;
+                    $last_last_nonblank_block_type = $last_nonblank_block_type;
+                    $last_last_nonblank_container_type =
+                      $last_nonblank_container_type;
+                    $last_last_nonblank_type_sequence =
+                      $last_nonblank_type_sequence;
+
+                    # Fix part #3 for git82: propagate type 'Z' though L-R pair
+                    unless ( $type eq 'R' && $last_nonblank_type eq 'Z' ) {
+                        $last_nonblank_token = $tok;
+                        $last_nonblank_type  = $type;
+                    }
+                    $last_nonblank_prototype      = $prototype;
+                    $last_nonblank_block_type     = $block_type;
+                    $last_nonblank_container_type = $container_type;
+                    $last_nonblank_type_sequence  = $type_sequence;
+                    $last_nonblank_i              = $i_tok;
+                }
 
                 # Patch for c030: Fix things in case a '->' got separated from
                 # the subsequent identifier by a side comment.  We need the
@@ -3499,7 +3718,25 @@ EOM
                     scan_identifier();
                 }
 
-                last if ($id_scan_state);
+                if ($id_scan_state) {
+
+                    # Still scanning ...
+                    # Check for side comment between sub and prototype (c061)
+
+                    # done if nothing left to scan on this line
+                    last if ( $i > $max_token_index );
+
+                    my ( $next_nonblank_token, $i_next ) =
+                      find_next_nonblank_token_on_this_line( $i, $rtokens,
+                        $max_token_index );
+
+                    # done if it was just some trailing space
+                    last if ( $i_next > $max_token_index );
+
+                    # something remains on the line ... must be a side comment
+                    next;
+                }
+
                 next if ( ( $i > 0 ) || $type );
 
                 # didn't find any token; start over
@@ -3729,8 +3966,18 @@ EOM
                            # a key with 18 a's.  But something like
                            #    push @array, a x18;
                            # is a syntax error.
-                            if ( $expecting == OPERATOR && $tok =~ /^x\d+$/ ) {
+                            if (
+                                   $expecting == OPERATOR
+                                && substr( $tok, 0, 1 ) eq 'x'
+                                && ( length($tok) == 1
+                                    || substr( $tok, 1, 1 ) =~ /^\d/ )
+                              )
+                            {
                                 $type = 'n';
+                                if ( split_pretoken(1) ) {
+                                    $type = 'x';
+                                    $tok  = 'x';
+                                }
                             }
                             else {
 
@@ -3786,13 +4033,14 @@ EOM
                 # Decide if 'sub :' can be the start of a sub attribute list.
                 # We will decide based on if the colon is followed by a
                 # bareword which is not a keyword.
+                # Changed inext+1 to inext to fixed case b1190.
                 my $sub_attribute_ok_here;
                 if (   $is_sub{$tok_kw}
                     && $expecting != OPERATOR
                     && $next_nonblank_token eq ':' )
                 {
                     my ( $nn_nonblank_token, $i_nn ) =
-                      find_next_nonblank_token( $i_next + 1,
+                      find_next_nonblank_token( $i_next,
                         $rtokens, $max_token_index );
                     $sub_attribute_ok_here =
                          $nn_nonblank_token =~ /^\w/
@@ -3801,12 +4049,15 @@ EOM
                 }
 
                 # handle operator x (now we know it isn't $x=)
-                if (   $expecting == OPERATOR
+                if (
+                       $expecting == OPERATOR
                     && substr( $tok, 0, 1 ) eq 'x'
-                    && $tok =~ /^x\d*$/ )
+                    && ( length($tok) == 1
+                        || substr( $tok, 1, 1 ) =~ /^\d/ )
+                  )
                 {
-                    if ( $tok eq 'x' ) {
 
+                    if ( $tok eq 'x' ) {
                         if ( $rtokens->[ $i + 1 ] eq '=' ) {    # x=
                             $tok  = 'x=';
                             $type = $tok;
@@ -3816,12 +4067,17 @@ EOM
                             $type = 'x';
                         }
                     }
-
-                    # NOTE: mark something like x4 as an integer for now
-                    # It gets fixed downstream.  This is easier than
-                    # splitting the pretoken.
                     else {
+
+                        # Split a pretoken like 'x10' into 'x' and '10'.
+                        # Note: In previous versions of perltidy it was marked
+                        # as a number, $type = 'n', and fixed downstream by the
+                        # Formatter.
                         $type = 'n';
+                        if ( split_pretoken(1) ) {
+                            $type = 'x';
+                            $tok  = 'x';
+                        }
                     }
                 }
                 elsif ( $tok_kw eq 'CORE::' ) {
@@ -4243,7 +4499,14 @@ EOM
                 if ( !defined($number) ) {
 
                     # shouldn't happen - we should always get a number
-                    warning("non-number beginning with digit--program bug\n");
+                    if (DEVEL_MODE) {
+                        Fault(<<EOM);
+non-number beginning with digit--program bug
+EOM
+                    }
+                    warning(
+"Unexpected error condition: non-number beginning with digit\n"
+                    );
                     report_definite_bug();
                 }
             }
@@ -4714,16 +4977,16 @@ EOM
 
                     # zero continuation flag at terminal BLOCK '}' which
                     # ends a statement.
-                    if ( $routput_block_type->[$i] ) {
+                    my $block_type_i = $routput_block_type->[$i];
+                    if ($block_type_i) {
 
                         # ...These include non-anonymous subs
                         # note: could be sub ::abc { or sub 'abc
-                        if ( $routput_block_type->[$i] =~ m/^sub\s*/gc ) {
+                        if ( $block_type_i =~ m/^sub\s*/gc ) {
 
                          # note: older versions of perl require the /gc modifier
                          # here or else the \G does not work.
-                            if ( $routput_block_type->[$i] =~ /\G('|::|\w)/gc )
-                            {
+                            if ( $block_type_i =~ /\G('|::|\w)/gc ) {
                                 $in_statement_continuation = 0;
                             }
                         }
@@ -4732,27 +4995,21 @@ EOM
 # block prototypes and these: (sort|grep|map|do|eval)
 # /^(\}|\{|BEGIN|END|CHECK|INIT|AUTOLOAD|DESTROY|UNITCHECK|continue|;|if|elsif|else|unless|while|until|for|foreach)$/
                         elsif (
-                            $is_zero_continuation_block_type{
-                                $routput_block_type->[$i]
-                            }
-                          )
+                            $is_zero_continuation_block_type{$block_type_i} )
                         {
                             $in_statement_continuation = 0;
                         }
 
                         # ..but these are not terminal types:
                         #     /^(sort|grep|map|do|eval)$/ )
-                        elsif (
-                            $is_not_zero_continuation_block_type{
-                                $routput_block_type->[$i]
-                            }
-                          )
+                        elsif ($is_sort_map_grep_eval_do{$block_type_i}
+                            || $is_grep_alias{$block_type_i} )
                         {
                         }
 
                         # ..and a block introduced by a label
                         # /^\w+\s*:$/gc ) {
-                        elsif ( $routput_block_type->[$i] =~ /:$/ ) {
+                        elsif ( $block_type_i =~ /:$/ ) {
                             $in_statement_continuation = 0;
                         }
 
@@ -4955,7 +5212,7 @@ EOM
 
         return;
     }
-}    # end tokenize_this_line
+} ## end tokenize_this_line
 
 #########i#############################################################
 # Tokenizer routines which assist in identifying token types
@@ -4967,6 +5224,10 @@ my %op_expected_table;
 # exceptions to perl's weird parsing rules after type 'Z'
 my %is_weird_parsing_rule_exception;
 
+my %is_paren_dollar;
+
+my %is_n_v;
+
 BEGIN {
 
     # Always expecting TERM following these types:
@@ -4997,7 +5258,13 @@ BEGIN {
 
     # Fix for git #62: added '*' and '%'
     @q = qw( < ? * % );
-    @{is_weird_parsing_rule_exception}{@q} = (OPERATOR) x scalar(@q);
+    @{is_weird_parsing_rule_exception}{@q} = (1) x scalar(@q);
+
+    @q = qw<) $>;
+    @{is_paren_dollar}{@q} = (1) x scalar(@q);
+
+    @q = qw( n v );
+    @{is_n_v}{@q} = (1) x scalar(@q);
 
 }
 
@@ -5087,7 +5354,8 @@ sub operator_expected {
         # FIXME: it would be cleaner to make this a special type
         # expecting VERSION or {} after package NAMESPACE
         # TODO: maybe mark these words as type 'Y'?
-        if (   $statement_type =~ /^package\b/
+        if (   substr( $last_nonblank_token, 0, 7 ) eq 'package'
+            && $statement_type      =~ /^package\b/
             && $last_nonblank_token =~ /^package\b/ )
         {
             $op_expected = TERM;
@@ -5150,10 +5418,12 @@ sub operator_expected {
             $op_expected = OPERATOR;    # block mode following }
         }
 
-        elsif ( $last_nonblank_token =~ /^(\)|\$|\-\>)/ ) {
+        ##elsif ( $last_nonblank_token =~ /^(\)|\$|\-\>)/ ) {
+        elsif ( $is_paren_dollar{ substr( $last_nonblank_token, 0, 1 ) }
+            || substr( $last_nonblank_token, 0, 2 ) eq '->' )
+        {
             $op_expected = OPERATOR;
             if ( $last_nonblank_token eq '$' ) { $op_expected = UNKNOWN }
-
         }
 
         # Check for smartmatch operator before preceding brace or square
@@ -5202,7 +5472,8 @@ sub operator_expected {
     #     use Module VERSION LIST
     # We could avoid this exception by writing a special sub to parse 'use'
     # statements and perhaps mark these numbers with a new type V (for VERSION)
-    elsif ( $last_nonblank_type =~ /^[nv]$/ ) {
+    ##elsif ( $last_nonblank_type =~ /^[nv]$/ ) {
+    elsif ( $is_n_v{$last_nonblank_type} ) {
         $op_expected = OPERATOR;
         if ( $statement_type eq 'use' ) {
             $op_expected = UNKNOWN;
@@ -5231,6 +5502,14 @@ sub operator_expected {
             $op_expected = UNKNOWN;
         }
 
+        # Exception to weird parsing rules for 'x(' ... see case b1205:
+        # In something like 'print $vv x(...' the x is an operator;
+        # Likewise in 'print $vv x$ww' the x is an operatory (case b1207)
+        # otherwise x follows the weird parsing rules.
+        elsif ( $tok eq 'x' && $next_type =~ /^[\(\$\@\%]$/ ) {
+            $op_expected = OPERATOR;
+        }
+
         # The 'weird parsing rules' of next section do not work for '<' and '?'
         # It is best to mark them as unknown.  Test case:
         #  print $fh <DATA>;
@@ -5397,7 +5676,9 @@ sub code_block_type {
 # otherwise, look at previous token.  This must be a code block if
 # it follows any of these:
 # /^(BEGIN|END|CHECK|INIT|AUTOLOAD|DESTROY|UNITCHECK|continue|if|elsif|else|unless|do|while|until|eval|for|foreach|map|grep|sort)$/
-    elsif ( $is_code_block_token{$last_nonblank_token} ) {
+    elsif ($is_code_block_token{$last_nonblank_token}
+        || $is_grep_alias{$last_nonblank_token} )
+    {
 
         # Bug Patch: Note that the opening brace after the 'if' in the following
         # snippet is an anonymous hash ref and not a code block!
@@ -5651,6 +5932,18 @@ sub report_unexpected {
     return;
 }
 
+my %is_sigil_or_paren;
+my %is_R_closing_sb;
+
+BEGIN {
+
+    my @q = qw< $ & % * @ ) >;
+    @{is_sigil_or_paren}{@q} = (1) x scalar(@q);
+
+    @q = qw(R ]);
+    @{is_R_closing_sb}{@q} = (1) x scalar(@q);
+}
+
 sub is_non_structural_brace {
 
     # Decide if a brace or bracket is structural or non-structural
@@ -5677,13 +5970,18 @@ sub is_non_structural_brace {
     # otherwise, it is non-structural if it is decorated
     # by type information.
     # For example, the '{' here is non-structural:   ${xxx}
+    # Removed '::' to fix c074
+    ## $last_nonblank_token =~ /^([\$\@\*\&\%\)]|->|::)/
     return (
-        $last_nonblank_token =~ /^([\$\@\*\&\%\)]|->|::)/
+        ## $last_nonblank_token =~ /^([\$\@\*\&\%\)]|->)/
+        $is_sigil_or_paren{ substr( $last_nonblank_token, 0, 1 ) }
+          || substr( $last_nonblank_token, 0, 2 ) eq '->'
 
           # or if we follow a hash or array closing curly brace or bracket
           # For example, the second '{' in this is non-structural: $a{'x'}{'y'}
           # because the first '}' would have been given type 'R'
-          || $last_nonblank_type =~ /^([R\]])$/
+          ##|| $last_nonblank_type =~ /^([R\]])$/
+          || $is_R_closing_sb{$last_nonblank_type}
     );
 }
 
@@ -5746,8 +6044,18 @@ sub increase_nesting_depth {
     # Sequence numbers increment by number of items.  This keeps
     # a unique set of numbers but still allows the relative location
     # of any type to be determined.
-    $nesting_sequence_number[$aa] += scalar(@closing_brace_names);
-    my $seqno = $nesting_sequence_number[$aa];
+
+    ########################################################################
+    # OLD SEQNO METHOD for incrementing sequence numbers.
+    # Keep this coding awhile for possible testing.
+    ## $nesting_sequence_number[$aa] += scalar(@closing_brace_names);
+    ## my $seqno = $nesting_sequence_number[$aa];
+
+    # NEW SEQNO METHOD, continuous sequence numbers. This allows sequence
+    # numbers to be used as array indexes, and allows them to be compared.
+    my $seqno = $next_sequence_number++;
+    ########################################################################
+
     $current_sequence_number[$aa][ $current_depth[$aa] ] = $seqno;
 
     $starting_line_of_current_depth[$aa][ $current_depth[$aa] ] =
@@ -5772,7 +6080,10 @@ sub increase_nesting_depth {
             }
         }
     }
-    $nested_statement_type[$aa][ $current_depth[$aa] ] = $statement_type;
+
+    # Fix part #1 for git82: save last token type for propagation of type 'Z'
+    $nested_statement_type[$aa][ $current_depth[$aa] ] =
+      [ $statement_type, $last_nonblank_type, $last_nonblank_token ];
     $statement_type = "";
     return ( $seqno, $indent );
 }
@@ -5818,7 +6129,19 @@ sub decrease_nesting_depth {
         if ( $aa == QUESTION_COLON ) {
             $outdent = $nested_ternary_flag[ $current_depth[$aa] ];
         }
-        $statement_type = $nested_statement_type[$aa][ $current_depth[$aa] ];
+
+        # Fix part #2 for git82: use saved type for propagation of type 'Z'
+        # through type L-R braces.  Perl seems to allow ${bareword}
+        # as an indirect object, but nothing much more complex than that.
+        ( $statement_type, my $saved_type, my $saved_token ) =
+          @{ $nested_statement_type[$aa][ $current_depth[$aa] ] };
+        if (   $aa == BRACE
+            && $saved_type eq 'Z'
+            && $last_nonblank_type eq 'w'
+            && $brace_structural_type[$brace_depth] eq 'L' )
+        {
+            $last_nonblank_type = $saved_type;
+        }
 
         # check that any brace types $bb contained within are balanced
         for my $bb ( 0 .. @closing_brace_names - 1 ) {
@@ -5963,8 +6286,9 @@ sub peek_ahead_for_nonblank_token {
         $line =~ s/^\s*//;                 # trim leading blanks
         next if ( length($line) <= 0 );    # skip blank
         next if ( $line =~ /^#/ );         # skip comment
-        my ( $rtok, $rmap, $rtype ) =
-          pre_tokenize( $line, 2 );        # only need 2 pre-tokens
+
+        # Updated from 2 to 3 to get trigraphs, added for case b1175
+        my ( $rtok, $rmap, $rtype ) = pre_tokenize( $line, 3 );
         my $j = $max_token_index + 1;
 
         foreach my $tok ( @{$rtok} ) {
@@ -6112,7 +6436,7 @@ sub guess_if_pattern_or_division {
         # usually indicates a pattern.  We can use this to break ties.
 
         my $is_pattern_by_spacing =
-          ( $i > 1 && $next_token ne ' ' && $rtokens->[ $i - 2 ] eq ' ' );
+          ( $i > 1 && $next_token !~ m/^\s/ && $rtokens->[ $i - 2 ] =~ m/^\s/ );
 
         # look for a possible ending / on this line..
         my $in_quote        = 1;
@@ -6597,8 +6921,13 @@ sub scan_id_do {
     if ( $id_scan_state && ( !defined($type) || !$type ) ) {
 
         # shouldn't happen:
+        if (DEVEL_MODE) {
+            Fault(<<EOM);
+Program bug in scan_id: undefined type but scan_state=$id_scan_state
+EOM
+        }
         warning(
-"Program bug in scan_id: undefined type but scan_state=$id_scan_state\n"
+"Possible program bug in sub scan_id: undefined type but scan_state=$id_scan_state\n"
         );
         report_definite_bug();
     }
@@ -6729,6 +7058,17 @@ sub do_scan_package {
     return ( $i, $tok, $type );
 }
 
+my %is_special_variable_char;
+
+BEGIN {
+
+    # These are the only characters which can (currently) form special
+    # variables, like $^W: (issue c066).
+    my @q =
+      qw{ ? A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ };
+    @{is_special_variable_char}{@q} = (1) x scalar(@q);
+}
+
 sub scan_identifier_do {
 
     # This routine assembles tokens into identifiers.  It maintains a
@@ -6805,11 +7145,16 @@ sub scan_identifier_do {
         }
         else {
 
-            # shouldn't happen
-            my ( $a, $b, $c ) = caller;
-            warning("Program Bug: scan_identifier given bad token = $tok \n");
-            warning("   called from sub $a  line: $c\n");
-            report_definite_bug();
+            # shouldn't happen: bad call parameter
+            my $msg =
+"Program bug detected: scan_identifier received bad starting token = '$tok'\n";
+            if (DEVEL_MODE) { Fault($msg) }
+            if ( !$tokenizer_self->[_in_error_] ) {
+                warning($msg);
+                $tokenizer_self->[_in_error_] = 1;
+            }
+            $id_scan_state = '';
+            goto RETURN;
         }
         $saw_type = !$saw_alpha;
     }
@@ -6984,26 +7329,41 @@ sub scan_identifier_do {
             }
             elsif ( $tok eq '^' ) {
 
-                # check for some special variables like $^W
+                # check for some special variables like $^ $^W
                 if ( $identifier =~ /^[\$\*\@\%]$/ ) {
                     $identifier .= $tok;
-                    $id_scan_state = 'A';
+                    $type = 'i';
 
-                    # Perl accepts '$^]' or '@^]', but
-                    # there must not be a space before the ']'.
+                    # There may be one more character, not a space, after the ^
                     my $next1 = $rtokens->[ $i + 1 ];
-                    if ( $next1 eq ']' ) {
+                    my $chr   = substr( $next1, 0, 1 );
+                    if ( $is_special_variable_char{$chr} ) {
+
+                        # It is something like $^W
+                        # Test case (c066) : $^Oeq'linux'
                         $i++;
                         $identifier .= $next1;
+
+                        # If pretoken $next1 is more than one character long,
+                        # set a flag indicating that it needs to be split.
+                        $id_scan_state = ( length($next1) > 1 ) ? '^' : "";
+                        last;
+                    }
+                    else {
+
+                        # it is just $^
+                        # Simple test case (c065): '$aa=$^if($bb)';
                         $id_scan_state = "";
                         last;
                     }
                 }
                 else {
                     $id_scan_state = '';
+                    $i             = $i_save;
+                    last;    # c106
                 }
             }
-            else {    # something else
+            else {           # something else
 
                 if ( $in_prototype_or_signature && $tok =~ /^[\),=#]/ ) {
 
@@ -7235,6 +7595,39 @@ sub scan_identifier_do {
                 $id_scan_state = '';
                 last;
             }
+            elsif ( $tok eq '^' ) {
+                if ( $identifier eq '&' ) {
+
+                    # Special variable (c066)
+                    $identifier .= $tok;
+                    $type = '&';
+
+                    # There may be one more character, not a space, after the ^
+                    my $next1 = $rtokens->[ $i + 1 ];
+                    my $chr   = substr( $next1, 0, 1 );
+                    if ( $is_special_variable_char{$chr} ) {
+
+                        # It is something like &^O
+                        $i++;
+                        $identifier .= $next1;
+
+                        # If pretoken $next1 is more than one character long,
+                        # set a flag indicating that it needs to be split.
+                        $id_scan_state = ( length($next1) > 1 ) ? '^' : "";
+                    }
+                    else {
+
+                        # it is &^
+                        $id_scan_state = "";
+                    }
+                    last;
+                }
+                else {
+                    $identifier = '';
+                    $i          = $i_save;
+                }
+                last;
+            }
             else {
 
                 # punctuation variable?
@@ -7298,7 +7691,14 @@ sub scan_identifier_do {
         if ($saw_type) {
 
             if ($saw_alpha) {
-                if ( $identifier =~ /^->/ && $last_nonblank_type eq 'w' ) {
+
+                # The type without the -> should be the same as with the -> so
+                # that if they get separated we get the same bond strengths,
+                # etc.  See b1234
+                if (   $identifier =~ /^->/
+                    && $last_nonblank_type eq 'w'
+                    && substr( $identifier, 2, 1 ) =~ /^\w/ )
+                {
                     $type = 'w';
                 }
                 else { $type = 'i' }
@@ -7343,6 +7743,8 @@ sub scan_identifier_do {
         $i   = $i_begin;
     }
 
+  RETURN:
+
     DEBUG_SCAN_ID && do {
         my ( $a, $b, $c ) = caller;
         print STDOUT
@@ -7582,15 +7984,16 @@ sub scan_identifier_do {
                     $max_token_index );
                 if ($error) { warning("Possibly invalid sub\n") }
 
-            # Patch part #2 to fixes cases b994 and b1053:
-            # Do not let spaces be part of the token of an anonymous sub keyword
-            # which we marked as type 'k' above...i.e. for something like:
-            #    'sub : lvalue { ...'
-            # Back up and let it be parsed as a blank
+                # Patch part #2 to fixes cases b994 and b1053:
+                # Do not let spaces be part of the token of an anonymous sub
+                # keyword which we marked as type 'k' above...i.e. for
+                # something like:
+                #    'sub : lvalue { ...'
+                # Back up and let it be parsed as a blank
                 if (   $type eq 'k'
                     && $attrs
                     && $i > $i_entry
-                    && substr( $rtokens->[$i], 0, 1 ) eq ' ' )
+                    && substr( $rtokens->[$i], 0, 1 ) =~ m/\s/ )
                 {
                     $i--;
                 }
@@ -7633,7 +8036,7 @@ sub scan_identifier_do {
                         else {
                             warning(
 "already saw definition of 'sub $subname' in package '$package' at line $lno\n"
-                            );
+                            ) unless (DEVEL_MODE);
                         }
                     }
                     $saw_function_definition{$subname}{$package} =
@@ -7718,6 +8121,42 @@ sub find_next_nonblank_token {
     return ( $next_nonblank_token, $i );
 }
 
+sub find_next_noncomment_type {
+    my ( $i, $rtokens, $max_token_index ) = @_;
+
+    # Given the current character position, look ahead past any comments
+    # and blank lines and return the next token, including digraphs and
+    # trigraphs.
+
+    my ( $next_nonblank_token, $i_next ) =
+      find_next_nonblank_token( $i, $rtokens, $max_token_index );
+
+    # skip past any side comment
+    if ( $next_nonblank_token eq '#' ) {
+        ( $next_nonblank_token, $i_next ) =
+          find_next_nonblank_token( $i_next, $rtokens, $max_token_index );
+    }
+
+    goto RETURN if ( !$next_nonblank_token || $next_nonblank_token eq " " );
+
+    # check for possible a digraph
+    goto RETURN if ( !defined( $rtokens->[ $i_next + 1 ] ) );
+    my $test2 = $next_nonblank_token . $rtokens->[ $i_next + 1 ];
+    goto RETURN if ( !$is_digraph{$test2} );
+    $next_nonblank_token = $test2;
+    $i_next              = $i_next + 1;
+
+    # check for possible a trigraph
+    goto RETURN if ( !defined( $rtokens->[ $i_next + 1 ] ) );
+    my $test3 = $next_nonblank_token . $rtokens->[ $i_next + 1 ];
+    goto RETURN if ( !$is_trigraph{$test3} );
+    $next_nonblank_token = $test3;
+    $i_next              = $i_next + 1;
+
+  RETURN:
+    return ( $next_nonblank_token, $i_next );
+}
+
 sub is_possible_numerator {
 
     # Look at the next non-comment character and decide if it could be a
@@ -7846,7 +8285,14 @@ sub find_angle_operator_termination {
     elsif ( $expecting == UNKNOWN ) { $filter = '[\>\;\=\#\|\<]' }
 
     # shouldn't happen - we shouldn't be here if operator is expected
-    else { warning("Program Bug in find_angle_operator_termination\n") }
+    else {
+        if (DEVEL_MODE) {
+            Fault(<<EOM);
+Bad call to find_angle_operator_termination
+EOM
+        }
+        return ( $i, $type );
+    }
 
     # To illustrate what we might be looking at, in case we are
     # guessing, here are some examples of valid angle operators
@@ -7886,6 +8332,22 @@ sub find_angle_operator_termination {
             my $pos_beg = $rtoken_map->[$i];
             my $str     = substr( $input_line, $pos_beg, ( $pos - $pos_beg ) );
 
+            # Test for '<' after possible filehandle, issue c103
+            # print $fh <>;          # syntax error
+            # print $fh <DATA>;      # ok
+            # print $fh < DATA>;     # syntax error at '>'
+            # print STDERR < DATA>;  # ok, prints word 'DATA'
+            # print BLABLA <DATA>;   # ok; does nothing unless BLABLA is defined
+            if ( $last_nonblank_type eq 'Z' ) {
+
+                # $str includes brackets; something like '<DATA>'
+                if (   substr( $last_nonblank_token, 0, 1 ) !~ /[A-Za-z_]/
+                    && substr( $str, 1, 1 ) !~ /[A-Za-z_]/ )
+                {
+                    return ( $i, $type );
+                }
+            }
+
             # Reject if the closing '>' follows a '-' as in:
             # if ( VERSION < 5.009 && $op-> name eq 'assign' ) { }
             if ( $expecting eq UNKNOWN ) {
@@ -7907,9 +8369,13 @@ sub find_angle_operator_termination {
             # It may be possible that a quote ends midway in a pretoken.
             # If this happens, it may be necessary to split the pretoken.
             if ($error) {
+                if (DEVEL_MODE) {
+                    Fault(<<EOM);
+unexpected error condition returned by inverse_pretoken_map
+EOM
+                }
                 warning(
                     "Possible tokinization error..please check this line\n");
-                report_possible_bug();
             }
 
             # count blanks on inside of brackets
@@ -8004,8 +8470,11 @@ sub scan_number_do {
 
     # Look for bad starting characters; Shouldn't happen..
     if ( $first_char !~ /[\d\.\+\-Ee]/ ) {
-        warning("Program bug - scan_number given character $first_char\n");
-        report_definite_bug();
+        if (DEVEL_MODE) {
+            Fault(<<EOM);
+Program bug - scan_number given bad first character = '$first_char'
+EOM
+        }
         return ( $i, $type, $number );
     }
 
@@ -8041,10 +8510,10 @@ sub scan_number_do {
            [Pp][+-]?[0-9a-fA-F]          # REQUIRED exponent with digit
            [0-9a-fA-F_]*)                # optional Additional exponent digits
 
-          # or hex integer
+           # or hex integer
            |([xX][0-9a-fA-F_]+)        
 
-          # or octal fraction
+           # or octal fraction
            |([oO]?[0-7_]+          # string of octal digits
            (\.([0-7][0-7_]*)?)?    # optional decimal and fraction
            [Pp][+-]?[0-7]          # REQUIRED exponent, no underscore
@@ -8059,7 +8528,7 @@ sub scan_number_do {
            [Pp][+-]?[01]           # Required exponent indicator, no underscore
            [01_]*)                 # additional exponent bits
 
-          # or binary integer
+           # or binary integer
            |([bB][01_]+)           # 'b' with string of binary digits 
 
            )/gx
@@ -8393,7 +8862,8 @@ sub follow_quoted_string {
                 "Note: alphanumeric quote delimiter ($beginning_tok) \n");
         }
 
-        while ( $i < $max_token_index ) {
+        # Note: changed < to <= here to fix c109. Relying on extra end blanks.
+        while ( $i <= $max_token_index ) {
 
             if ( $quote_pos == 0 || ( $i < 0 ) ) {
                 $tok = $rtokens->[ ++$i ];
@@ -8421,6 +8891,11 @@ sub follow_quoted_string {
                 $quoted_string .=
                   substr( $tok, $old_pos, $quote_pos - $old_pos - 1 );
 
+                # NOTE: any quote modifiers will be at the end of '$tok'. If we
+                # wanted to check them, this is the place to get them.  But
+                # this quote form is rarely used in practice, so it isn't
+                # worthwhile.
+
                 $quote_depth--;
 
                 if ( $quote_depth == 0 ) {
@@ -8732,6 +9207,8 @@ The following additional token types are defined:
     HERE_END       - last line of here-doc (target word)
     FORMAT         - format section
     FORMAT_END     - last line of format section, '.'
+    SKIP           - code skipping section
+    SKIP_END       - last line of code skipping section, '#>>V'
     DATA_START     - __DATA__ line
     DATA           - unidentified text following __DATA__
     END_START      - __END__ line
@@ -8800,6 +9277,13 @@ BEGIN {
       switch case given when default catch try finally);
     @is_code_block_token{@q} = (1) x scalar(@q);
 
+    # Note: this hash was formerly named '%is_not_zero_continuation_block_type'
+    # to contrast it with the block types in '%is_zero_continuation_block_type'
+    @q = qw( sort map grep eval do );
+    @is_sort_map_grep_eval_do{@q} = (1) x scalar(@q);
+
+    %is_grep_alias = ();
+
     # I'll build the list of keywords incrementally
     my @Keywords = ();
 
index 7613ee29dfb55c04b94ff20b4ebb990a82093336..1bb9c482e6dd469c103658626f34a23b56afa6e1 100644 (file)
@@ -1,11 +1,13 @@
 package Perl::Tidy::VerticalAligner;
 use strict;
 use warnings;
-our $VERSION = '20210717';
-
+use Carp;
+our $VERSION = '20220217';
 use Perl::Tidy::VerticalAligner::Alignment;
 use Perl::Tidy::VerticalAligner::Line;
 
+use constant DEVEL_MODE => 0;
+
 # The Perl::Tidy::VerticalAligner package collects output lines and
 # attempts to line up certain common tokens, such as => and #, which are
 # identified by the calling routine.
@@ -80,11 +82,47 @@ sub DESTROY {
     # required to avoid call to AUTOLOAD in some versions of perl
 }
 
+sub Die {
+    my ($msg) = @_;
+    Perl::Tidy::Die($msg);
+    croak "unexpected return from Perl::Tidy::Die";
+}
+
+sub Fault {
+    my ($msg) = @_;
+
+    # This routine is called for errors that really should not occur
+    # except if there has been a bug introduced by a recent program change.
+    # Please add comments at calls to Fault to explain why the call
+    # should not occur, and where to look to fix it.
+    my ( $package0, $filename0, $line0, $subroutine0 ) = caller(0);
+    my ( $package1, $filename1, $line1, $subroutine1 ) = caller(1);
+    my ( $package2, $filename2, $line2, $subroutine2 ) = caller(2);
+    my $input_stream_name = get_input_stream_name();
+
+    Die(<<EOM);
+==============================================================================
+While operating on input stream with name: '$input_stream_name'
+A fault was detected at line $line0 of sub '$subroutine1'
+in file '$filename1'
+which was called from line $line1 of sub '$subroutine2'
+Message: '$msg'
+This is probably an error introduced by a recent programming change.
+Perl::Tidy::VerticalAligner.pm reports VERSION='$VERSION'.
+==============================================================================
+EOM
+
+    # We shouldn't get here, but this return is to keep Perl-Critic from
+    # complaining.
+    return;
+}
+
 BEGIN {
 
     # Define the fixed indexes for variables in $self, which is an array
     # reference.  Note the convention of leading and trailing underscores to
     # keep them unique.
+    # Do not combine with other BEGIN blocks (c101).
     my $i = 0;
     use constant {
         _file_writer_object_ => $i++,
@@ -92,15 +130,15 @@ BEGIN {
         _diagnostics_object_ => $i++,
         _length_function_    => $i++,
 
-        _rOpts_                              => $i++,
-        _rOpts_indent_columns_               => $i++,
-        _rOpts_tabs_                         => $i++,
-        _rOpts_entab_leading_whitespace_     => $i++,
-        _rOpts_fixed_position_side_comment_  => $i++,
-        _rOpts_minimum_space_to_comment_     => $i++,
-        _rOpts_maximum_line_length_          => $i++,
-        _rOpts_variable_maximum_line_length_ => $i++,
-        _rOpts_valign_                       => $i++,
+        _rOpts_                             => $i++,
+        _rOpts_indent_columns_              => $i++,
+        _rOpts_tabs_                        => $i++,
+        _rOpts_entab_leading_whitespace_    => $i++,
+        _rOpts_fixed_position_side_comment_ => $i++,
+        _rOpts_minimum_space_to_comment_    => $i++,
+        _rOpts_valign_code_                 => $i++,
+        _rOpts_valign_block_comments_       => $i++,
+        _rOpts_valign_side_comments_        => $i++,
 
         _last_level_written_            => $i++,
         _last_side_comment_column_      => $i++,
@@ -115,6 +153,7 @@ BEGIN {
         _rgroup_lines_                => $i++,
         _group_level_                 => $i++,
         _group_type_                  => $i++,
+        _group_maximum_line_length_   => $i++,
         _zero_count_                  => $i++,
         _last_leading_space_count_    => $i++,
         _comment_leading_space_count_ => $i++,
@@ -133,7 +172,68 @@ BEGIN {
     };
 
     DEBUG_TABS && $debug_warning->('TABS');
+}
+
+# GLOBAL variables
+my (
+
+    %valign_control_hash,
+    $valign_control_default,
+
+);
+
+sub check_options {
+
+    # This routine is called to check the user-supplied run parameters
+    # and to configure the control hashes to them.
+    my ($rOpts) = @_;
+
+    # All alignments are done by default
+    %valign_control_hash    = ();
+    $valign_control_default = 1;
+
+    # If -vil=s is entered without -vxl, assume -vxl='*'
+    if (  !$rOpts->{'valign-exclusion-list'}
+        && $rOpts->{'valign-inclusion-list'} )
+    {
+        $rOpts->{'valign-exclusion-list'} = '*';
+    }
+
+    # See if the user wants to exclude any alignment types ...
+    if ( $rOpts->{'valign-exclusion-list'} ) {
+
+        # The inclusion list is only relevant if there is an exclusion list
+        if ( $rOpts->{'valign-inclusion-list'} ) {
+            my @vil = split /\s+/, $rOpts->{'valign-inclusion-list'};
+            @valign_control_hash{@vil} = (1) x scalar(@vil);
+        }
+
+        # Note that the -vxl list is done after -vil, so -vxl has priority
+        # in the event of duplicate entries.
+        my @vxl = split /\s+/, $rOpts->{'valign-exclusion-list'};
+        @valign_control_hash{@vxl} = (0) x scalar(@vxl);
+
+        # Optimization: revert to defaults if no exclusions.
+        # This could happen with -vxl='  ' and any -vil list
+        if ( !@vxl ) {
+            %valign_control_hash = ();
+        }
+
+        # '$valign_control_default' applies to types not in the hash:
+        # - If a '*' was entered then set it to be that default type
+        # - Otherwise, leave it set it to 1
+        if ( defined( $valign_control_hash{'*'} ) ) {
+            $valign_control_default = $valign_control_hash{'*'};
+        }
+
+        # Side comments are controlled separately and must be removed
+        # if given in a list.
+        if (%valign_control_hash) {
+            $valign_control_hash{'#'} = 1;
+        }
+    }
 
+    return;
 }
 
 sub new {
@@ -154,6 +254,7 @@ sub new {
     initialize_valign_buffer();
     initialize_leading_string_cache();
     initialize_decode();
+    set_logger_object( $args{logger_object} );
 
     # Initialize all variables in $self.
     # To add an item to $self, first define a new constant index in the BEGIN
@@ -178,15 +279,15 @@ sub new {
       $rOpts->{'fixed-position-side-comment'};
     $self->[_rOpts_minimum_space_to_comment_] =
       $rOpts->{'minimum-space-to-comment'};
-    $self->[_rOpts_maximum_line_length_] = $rOpts->{'maximum-line-length'};
-    $self->[_rOpts_variable_maximum_line_length_] =
-      $rOpts->{'variable-maximum-line-length'};
-    $self->[_rOpts_valign_] = $rOpts->{'valign'};
+    $self->[_rOpts_valign_code_]           = $rOpts->{'valign-code'};
+    $self->[_rOpts_valign_block_comments_] = $rOpts->{'valign-block-comments'};
+    $self->[_rOpts_valign_side_comments_]  = $rOpts->{'valign-side-comments'};
 
     # Batch of lines being collected
     $self->[_rgroup_lines_]                = [];
     $self->[_group_level_]                 = 0;
     $self->[_group_type_]                  = "";
+    $self->[_group_maximum_line_length_]   = undef;
     $self->[_zero_count_]                  = 0;
     $self->[_comment_leading_space_count_] = 0;
     $self->[_last_leading_space_count_]    = 0;
@@ -237,6 +338,7 @@ sub initialize_for_new_group {
     $self->[_zero_count_]                  = 0;
     $self->[_comment_leading_space_count_] = 0;
     $self->[_last_leading_space_count_]    = 0;
+    $self->[_group_maximum_line_length_]   = undef;
 
     # Note that the value for _group_level_ is
     # handled separately in sub valign_input
@@ -258,32 +360,42 @@ sub write_diagnostics {
     return;
 }
 
-# interface to Perl::Tidy::Logger routines
-sub warning {
-    my ( $self, $msg ) = @_;
-    my $logger_object = $self->[_logger_object_];
-    if ($logger_object) {
-        $logger_object->warning($msg);
+{    ## begin closure for logger routines
+    my $logger_object;
+
+    # Called once per file to initialize the logger object
+    sub set_logger_object {
+        $logger_object = shift;
+        return;
     }
-    return;
-}
 
-sub write_logfile_entry {
-    my ( $self, $msg ) = @_;
-    my $logger_object = $self->[_logger_object_];
-    if ($logger_object) {
-        $logger_object->write_logfile_entry($msg);
+    sub get_logger_object {
+        return $logger_object;
     }
-    return;
-}
 
-sub report_definite_bug {
-    my ( $self, $msg ) = @_;
-    my $logger_object = $self->[_logger_object_];
-    if ($logger_object) {
-        $logger_object->report_definite_bug();
+    sub get_input_stream_name {
+        my $input_stream_name = "";
+        if ($logger_object) {
+            $input_stream_name = $logger_object->get_input_stream_name();
+        }
+        return $input_stream_name;
+    }
+
+    sub warning {
+        my ($msg) = @_;
+        if ($logger_object) {
+            $logger_object->warning($msg);
+        }
+        return;
+    }
+
+    sub write_logfile_entry {
+        my ($msg) = @_;
+        if ($logger_object) {
+            $logger_object->write_logfile_entry($msg);
+        }
+        return;
     }
-    return;
 }
 
 sub get_cached_line_count {
@@ -291,15 +403,6 @@ sub get_cached_line_count {
     return $self->group_line_count() + ( get_cached_line_type() ? 1 : 0 );
 }
 
-sub get_spaces {
-
-    # return the number of leading spaces associated with an indentation
-    # variable $indentation is either a constant number of spaces or an
-    # object with a get_spaces method.
-    my $indentation = shift;
-    return ref($indentation) ? $indentation->get_spaces() : $indentation;
-}
-
 sub get_recoverable_spaces {
 
     # return the number of spaces (+ means shift right, - means shift left)
@@ -309,18 +412,6 @@ sub get_recoverable_spaces {
     return ref($indentation) ? $indentation->get_recoverable_spaces() : 0;
 }
 
-sub maximum_line_length_for_level {
-
-    # return maximum line length for line starting with a given level
-    my ( $self, $level ) = @_;
-    my $maximum_line_length = $self->[_rOpts_maximum_line_length_];
-    if ( $self->[_rOpts_variable_maximum_line_length_] ) {
-        if ( $level < 0 ) { $level = 0 }
-        $maximum_line_length += $level * $self->[_rOpts_indent_columns_];
-    }
-    return $maximum_line_length;
-}
-
 ######################################################
 # CODE SECTION 3: Code to accept input and form groups
 ######################################################
@@ -336,15 +427,23 @@ sub push_group_line {
 use constant DEBUG_VALIGN      => 0;
 use constant SC_LONG_LINE_DIFF => 12;
 
+my %is_closing_token;
+
+BEGIN {
+    my @q = qw< } ) ] >;
+    @is_closing_token{@q} = (1) x scalar(@q);
+}
+
 sub valign_input {
 
     # Place one line in the current vertical group.
     #
-    # The input parameters are:
-    #     $level = indentation level of this line
-    #     $rfields = reference to array of fields
-    #     $rpatterns = reference to array of patterns, one per field
-    #     $rtokens   = reference to array of tokens starting fields 1,2,..
+    # The key input parameters describing each line are:
+    #     $level          = indentation level of this line
+    #     $rfields        = ref to array of fields
+    #     $rpatterns      = ref to array of patterns, one per field
+    #     $rtokens        = ref to array of tokens starting fields 1,2,..
+    #     $rfield_lengths = ref to array of field display widths
     #
     # Here is an example of what this package does.  In this example,
     # we are trying to line up both the '=>' and the '#'.
@@ -393,23 +492,21 @@ sub valign_input {
 
     my $level                     = $rline_hash->{level};
     my $level_end                 = $rline_hash->{level_end};
-    my $level_adj                 = $rline_hash->{level_adj};
     my $indentation               = $rline_hash->{indentation};
     my $list_seqno                = $rline_hash->{list_seqno};
     my $outdent_long_lines        = $rline_hash->{outdent_long_lines};
     my $is_terminal_ternary       = $rline_hash->{is_terminal_ternary};
     my $rvertical_tightness_flags = $rline_hash->{rvertical_tightness_flags};
-    my $level_jump                = $rline_hash->{level_jump};
-    my $rfields                   = $rline_hash->{rfields};
-    my $rtokens                   = $rline_hash->{rtokens};
-    my $rpatterns                 = $rline_hash->{rpatterns};
-    my $rfield_lengths            = $rline_hash->{rfield_lengths};
-    my $terminal_block_type       = $rline_hash->{terminal_block_type};
-    my $batch_count               = $rline_hash->{batch_count};
     my $break_alignment_before    = $rline_hash->{break_alignment_before};
     my $break_alignment_after     = $rline_hash->{break_alignment_after};
     my $Kend                      = $rline_hash->{Kend};
     my $ci_level                  = $rline_hash->{ci_level};
+    my $maximum_line_length       = $rline_hash->{maximum_line_length};
+    my $forget_side_comment       = $rline_hash->{forget_side_comment};
+    my $rline_alignment           = $rline_hash->{rline_alignment};
+
+    my ( $rtokens, $rfields, $rpatterns, $rfield_lengths ) =
+      @{$rline_alignment};
 
     # The index '$Kend' is a value which passed along with the line text to sub
     # 'write_code_line' for a convergence check.
@@ -418,7 +515,8 @@ sub valign_input {
     # number of tokens between fields is $jmax-1
     my $jmax = @{$rfields} - 1;
 
-    my $leading_space_count = get_spaces($indentation);
+    my $leading_space_count =
+      ref($indentation) ? $indentation->get_spaces() : $indentation;
 
     # set outdented flag to be sure we either align within statements or
     # across statement boundaries, but not both.
@@ -455,16 +553,19 @@ sub valign_input {
 
     # Reset side comment location if we are entering a new block from level 0.
     # This is intended to keep them from drifting too far to the right.
-    if ( $terminal_block_type && $level_adj == 0 && $level_end > $level ) {
+    if ($forget_side_comment) {
         $self->forget_side_comment();
     }
 
-    my $group_level = $self->[_group_level_];
+    my $is_balanced_line = $level_end == $level;
+
+    my $group_level               = $self->[_group_level_];
+    my $group_maximum_line_length = $self->[_group_maximum_line_length_];
 
     DEBUG_VALIGN && do {
         my $nlines = $self->group_line_count();
         print STDOUT
-"Entering valign_input: lines=$nlines new #fields= $jmax, leading_count=$leading_space_count, level_jump=$level_jump, level=$level, group_level=$group_level, level_jump=$level_jump\n";
+"Entering valign_input: lines=$nlines new #fields= $jmax, leading_count=$leading_space_count, level=$level, group_level=$group_level, level_end=$level_end\n";
     };
 
     # Validate cached line if necessary: If we can produce a container
@@ -473,16 +574,29 @@ sub valign_input {
     # cached flags as valid.
     my $cached_line_type = get_cached_line_type();
     if ($cached_line_type) {
-        my $cached_line_flag = get_cached_line_flag();
+        my $cached_line_opening_flag = get_cached_line_opening_flag();
         if ($rvertical_tightness_flags) {
             my $cached_seqno = get_cached_seqno();
             if (   $cached_seqno
-                && $self->group_line_count() <= 1
-                && $rvertical_tightness_flags->[2]
-                && $rvertical_tightness_flags->[2] == $cached_seqno )
+                && $rvertical_tightness_flags->{_vt_seqno}
+                && $rvertical_tightness_flags->{_vt_seqno} == $cached_seqno )
             {
-                $rvertical_tightness_flags->[3] ||= 1;
-                set_cached_line_valid(1);
+
+                # Fix for b1187 and b1188: Normally this step is only done
+                # if the number of existing lines is 0 or 1.  But to prevent
+                # blinking, this range can be controlled by the caller.
+                # If zero values are given we fall back on the range 0 to 1.
+                my $line_count = $self->group_line_count();
+                my $min_lines  = $rvertical_tightness_flags->{_vt_min_lines};
+                my $max_lines  = $rvertical_tightness_flags->{_vt_max_lines};
+                $min_lines = 0 unless ($min_lines);
+                $max_lines = 1 unless ($max_lines);
+                if (   ( $line_count >= $min_lines )
+                    && ( $line_count <= $max_lines ) )
+                {
+                    $rvertical_tightness_flags->{_vt_valid_flag} ||= 1;
+                    set_cached_line_valid(1);
+                }
             }
         }
 
@@ -490,8 +604,8 @@ sub valign_input {
         # unless requested with a flag value of 2
         if (   $cached_line_type == 3
             && !$self->group_line_count()
-            && $cached_line_flag < 2
-            && $level_jump != 0 )
+            && $cached_line_opening_flag < 2
+            && !$is_balanced_line )
         {
             set_cached_line_valid(0);
         }
@@ -501,19 +615,30 @@ sub valign_input {
     if ( $level < 0 ) { $level = 0 }
 
     # do not align code across indentation level changes
-    # or if vertical alignment is turned off for debugging
-    if ( $level != $group_level || $is_outdented || !$self->[_rOpts_valign_] ) {
+    # or changes in the maximum line length
+    # or if vertical alignment is turned off
+    if (
+        $level != $group_level
+        || (   $group_maximum_line_length
+            && $maximum_line_length != $group_maximum_line_length )
+        || $is_outdented
+        || ( $is_block_comment && !$self->[_rOpts_valign_block_comments_] )
+        || (   !$is_block_comment
+            && !$self->[_rOpts_valign_side_comments_]
+            && !$self->[_rOpts_valign_code_] )
+      )
+    {
 
         $self->_flush_group_lines( $level - $group_level );
 
-        $group_level = $level;
-        $self->[_group_level_] = $group_level;
-
-        # wait until after the above flush to get the leading space
-        # count because it may have been changed if the -icp flag is in
-        # effect
-        $leading_space_count = get_spaces($indentation);
+        $group_level                         = $level;
+        $self->[_group_level_]               = $group_level;
+        $self->[_group_maximum_line_length_] = $maximum_line_length;
 
+        # Update leading spaces after the above flush because the leading space
+        # count may have been changed if the -icp flag is in effect
+        $leading_space_count =
+          ref($indentation) ? $indentation->get_spaces() : $indentation;
     }
 
     # --------------------------------------------------------------------
@@ -569,7 +694,7 @@ sub valign_input {
     # alignment of the '{'.
     if (   $rfields->[0] eq 'else '
         && @{$rgroup_lines}
-        && $level_jump == 0 )
+        && $is_balanced_line )
     {
 
         $j_terminal_match =
@@ -605,6 +730,7 @@ sub valign_input {
         {
             $self->[_group_type_]                  = 'COMMENT';
             $self->[_comment_leading_space_count_] = $leading_space_count;
+            $self->[_group_maximum_line_length_]   = $maximum_line_length;
             $self->push_group_line(
                 [ $rfields->[0], $rfield_lengths->[0], $Kend ] );
             return;
@@ -627,9 +753,9 @@ sub valign_input {
                     level                     => $level,
                     level_end                 => $level_end,
                     Kend                      => $Kend,
+                    maximum_line_length       => $maximum_line_length,
                 }
             );
-
             return;
         }
     }
@@ -637,15 +763,17 @@ sub valign_input {
         $self->[_zero_count_] = 0;
     }
 
-    my $maximum_line_length_for_level =
-      $self->maximum_line_length_for_level($level);
-
     # --------------------------------------------------------------------
     # It simplifies things to create a zero length side comment
     # if none exists.
     # --------------------------------------------------------------------
-    $self->make_side_comment( $rtokens, $rfields, $rpatterns, $rfield_lengths );
-    $jmax = @{$rfields} - 1;
+    if ( ( $jmax == 0 ) || ( $rtokens->[ $jmax - 1 ] ne '#' ) ) {
+        $jmax += 1;
+        $rtokens->[ $jmax - 1 ]  = '#';
+        $rfields->[$jmax]        = '';
+        $rfield_lengths->[$jmax] = 0;
+        $rpatterns->[$jmax]      = '#';
+    }
 
     # --------------------------------------------------------------------
     # create an object to hold this line
@@ -663,7 +791,6 @@ sub valign_input {
             list_seqno                => $list_seqno,
             list_type                 => "",
             is_hanging_side_comment   => $is_hanging_side_comment,
-            maximum_line_length       => $maximum_line_length_for_level,
             rvertical_tightness_flags => $rvertical_tightness_flags,
             is_terminal_ternary       => $is_terminal_ternary,
             j_terminal_match          => $j_terminal_match,
@@ -673,6 +800,7 @@ sub valign_input {
             level                     => $level,
             level_end                 => $level_end,
             imax_pair                 => -1,
+            maximum_line_length       => $maximum_line_length,
         }
     );
 
@@ -687,6 +815,7 @@ sub valign_input {
     # --------------------------------------------------------------------
 
     $self->push_group_line($new_line);
+    $self->[_group_maximum_line_length_] = $maximum_line_length;
 
     # output this group if it ends in a terminal else or ternary line
     if ( defined($j_terminal_match) ) {
@@ -694,8 +823,10 @@ sub valign_input {
     }
 
     # Force break after jump to lower level
-    if ( $level_jump < 0 ) {
-        $self->_flush_group_lines($level_jump);
+    elsif ($level_end < $level
+        || $is_closing_token{ substr( $rfields->[0], 0, 1 ) } )
+    {
+        $self->_flush_group_lines(-1);
     }
 
     # --------------------------------------------------------------------
@@ -758,25 +889,6 @@ sub join_hanging_comment {
     return 1;
 }
 
-sub make_side_comment {
-
-    # create an empty side comment if none exists
-
-    my ( $self, $rtokens, $rfields, $rpatterns, $rfield_lengths ) = @_;
-
-    my $jmax = @{$rfields} - 1;
-
-    # if line does not have a side comment...
-    if ( ( $jmax == 0 ) || ( $rtokens->[ $jmax - 1 ] ne '#' ) ) {
-        $jmax += 1;
-        $rtokens->[ $jmax - 1 ]  = '#';
-        $rfields->[$jmax]        = '';
-        $rfield_lengths->[$jmax] = 0;
-        $rpatterns->[$jmax]      = '#';
-    }
-    return;
-}
-
 {    ## closure for sub decide_if_list
 
     my %is_comma_token;
@@ -838,6 +950,12 @@ sub fix_terminal_ternary {
     return unless ($old_line);
     use constant EXPLAIN_TERNARY => 0;
 
+    if (%valign_control_hash) {
+        my $align_ok = $valign_control_hash{'?'};
+        $align_ok = $valign_control_default unless defined($align_ok);
+        return unless ($align_ok);
+    }
+
     my $jmax        = @{$rfields} - 1;
     my $rfields_old = $old_line->get_rfields();
 
@@ -1007,6 +1125,12 @@ sub fix_terminal_else {
     my $jmax = @{$rfields} - 1;
     return unless ( $jmax > 0 );
 
+    if (%valign_control_hash) {
+        my $align_ok = $valign_control_hash{'{'};
+        $align_ok = $valign_control_default unless defined($align_ok);
+        return unless ($align_ok);
+    }
+
     # check for balanced else block following if/elsif/unless
     my $rfields_old = $old_line->get_rfields();
 
@@ -1174,6 +1298,7 @@ sub check_fit {
     my $rfield_lengths      = $new_line->get_rfield_lengths();
     my $padding_available   = $old_line->get_available_space_on_right();
     my $jmax_old            = $old_line->get_jmax();
+    my $rtokens_old         = $old_line->get_rtokens();
 
     # Safety check ... only lines with equal array sizes should arrive here
     # from sub check_match.  So if this error occurs, look at recent changes in
@@ -1181,7 +1306,7 @@ sub check_fit {
     # identical numbers of alignment tokens.
     if ( $jmax_old ne $jmax ) {
 
-        $self->warning(<<EOM);
+        warning(<<EOM);
 Program bug detected in Perl::Tidy::VerticalAligner sub check_fit 
 unexpected difference in array lengths: $jmax != $jmax_old
 EOM
@@ -1207,9 +1332,9 @@ EOM
         }
 
         # Keep going if this field does not need any space.
-        next if $pad < 0;
+        next if ( $pad < 0 );
 
-        # See if it needs too much space.
+        # Revert to the starting state if does not fit
         if ( $pad > $padding_available ) {
 
             ################################################
@@ -1295,8 +1420,9 @@ sub _flush_comment_lines {
     my ($self) = @_;
     my $rgroup_lines = $self->[_rgroup_lines_];
     return unless ( @{$rgroup_lines} );
-    my $group_level         = $self->[_group_level_];
-    my $leading_space_count = $self->[_comment_leading_space_count_];
+    my $group_level               = $self->[_group_level_];
+    my $group_maximum_line_length = $self->[_group_maximum_line_length_];
+    my $leading_space_count       = $self->[_comment_leading_space_count_];
     my $leading_string =
       $self->get_leading_string( $leading_space_count, $group_level );
 
@@ -1305,9 +1431,7 @@ sub _flush_comment_lines {
     foreach my $item ( @{$rgroup_lines} ) {
         my ( $str, $str_len ) = @{$item};
         my $excess =
-          $str_len +
-          $leading_space_count -
-          $self->maximum_line_length_for_level($group_level);
+          $str_len + $leading_space_count - $group_maximum_line_length;
         if ( $excess > $max_excess ) {
             $max_excess = $excess;
         }
@@ -1320,12 +1444,13 @@ sub _flush_comment_lines {
         my $file_writer_object = $self->[_file_writer_object_];
         my $last_outdented_line_at =
           $file_writer_object->get_output_line_number();
-        $self->[_last_outdented_line_at_] = $last_outdented_line_at;
+        my $nlines = @{$rgroup_lines};
+        $self->[_last_outdented_line_at_] =
+          $last_outdented_line_at + $nlines - 1;
         my $outdented_line_count = $self->[_outdented_line_count_];
         unless ($outdented_line_count) {
             $self->[_first_outdented_line_at_] = $last_outdented_line_at;
         }
-        my $nlines = @{$rgroup_lines};
         $outdented_line_count += $nlines;
         $self->[_outdented_line_count_] = $outdented_line_count;
     }
@@ -1342,10 +1467,11 @@ sub _flush_comment_lines {
                 line_length               => $str_len,
                 side_comment_length       => 0,
                 outdent_long_lines        => $outdent_long_lines,
-                rvertical_tightness_flags => "",
+                rvertical_tightness_flags => undef,
                 level                     => $group_level,
                 level_end                 => $group_level,
                 Kend                      => $Kend,
+                maximum_line_length       => $group_maximum_line_length,
             }
         );
     }
@@ -1366,6 +1492,7 @@ sub _flush_group_lines {
 
     # $level_jump = $next_level-$group_level, if known
     #             = undef if not known
+    # Note: only the sign of the jump is needed
 
     my $rgroup_lines = $self->[_rgroup_lines_];
     return unless ( @{$rgroup_lines} );
@@ -1441,8 +1568,11 @@ sub _flush_group_lines {
       : 0;
 
     # STEP 6: Output the lines.
-    # All lines in this batch have the same basic leading spacing:
+    # All lines in this group have the same leading spacing and maximum line
+    # length
     my $group_leader_length = $rgroup_lines->[0]->get_leading_space_count();
+    my $group_maximum_line_length =
+      $rgroup_lines->[0]->get_maximum_line_length();
 
     foreach my $line ( @{$rgroup_lines} ) {
         $self->valign_output_step_A(
@@ -1453,10 +1583,17 @@ sub _flush_group_lines {
                 group_leader_length  => $group_leader_length,
                 extra_leading_spaces => $extra_leading_spaces,
                 level                => $group_level,
+                maximum_line_length  => $group_maximum_line_length,
             }
         );
     }
 
+    # Let the formatter know that this object has been processed and any
+    # recoverable spaces have been handled.  This is needed for setting the
+    # closing paren location in -lp mode.
+    my $object = $rgroup_lines->[0]->get_indentation();
+    if ( ref($object) ) { $object->set_recoverable_spaces(0) }
+
     $self->initialize_for_new_group();
     return;
 }
@@ -1590,7 +1727,7 @@ sub _flush_group_lines {
             if ( !defined($jbeg) ) {
 
                 # safety check, shouldn't happen
-                $self->warning(<<EOM);
+                warning(<<EOM);
 Program bug detected in Perl::Tidy::VerticalAligner sub sweep_top_down 
 undefined index for group line count $group_line_count
 EOM
@@ -2031,6 +2168,7 @@ sub sweep_left_to_right {
                     # spot to take special action on failure to move
                 }
             }
+            return;
         };
 
         foreach my $task ( @{$rtodo} ) {
@@ -2494,8 +2632,8 @@ EOM
                 $i++;
             }
             push @{$rline_hashes}, $rhash;
-            push @equals_info, [ $i_eq, $tok_eq, $pat_eq ];
-            push @line_info, [ $lev_min, $lev_max ];
+            push @equals_info,     [ $i_eq,    $tok_eq, $pat_eq ];
+            push @line_info,       [ $lev_min, $lev_max ];
             if ( defined($lev_min) ) {
                 my $lev_diff = $lev_max - $lev_min;
                 if ( $lev_diff > $max_lev_diff ) { $max_lev_diff = $lev_diff }
@@ -2675,6 +2813,16 @@ EOM
                     #######################################################
                     my $delete_me = !defined($il) && !defined($ir);
 
+                    # Apply any user controls. Note that not all lines pass
+                    # this way so they have to be applied elsewhere too.
+                    my $align_ok = 1;
+                    if (%valign_control_hash) {
+                        $align_ok = $valign_control_hash{$raw_tok};
+                        $align_ok = $valign_control_default
+                          unless defined($align_ok);
+                        $delete_me ||= !$align_ok;
+                    }
+
                     # But now we modify this with exceptions...
 
                     # EXCEPTION 1: If we are in a complete ternary or
@@ -2703,8 +2851,8 @@ EOM
                     # will now be incorrect. For example, this will prevent
                     # aligning commas as follows after deleting the second '=>'
                     #    $w->insert(
-                    #  ListBox => origin => [ 270, 160 ],
-                    #  size    => [ 200,           55 ],
+                    #         ListBox => origin => [ 270, 160 ],
+                    #         size    => [ 200,           55 ],
                     #    );
                     if ( defined($delete_above_level) ) {
                         if ( $lev > $delete_above_level ) {
@@ -2771,6 +2919,9 @@ EOM
                         }
                     }
 
+                    # Do not let a user exclusion be reactivated by above rules
+                    $delete_me ||= !$align_ok;
+
                     #####################################
                     # Add this token to the deletion list
                     #####################################
@@ -2911,6 +3062,7 @@ sub delete_null_alignments {
             my $length_match = $rfield_lengths_match->[$i];
             if ( $length ne $length_match ) { $rneed_pad->[$i] = 1 }
         }
+        return;
     };
 
     my $end_match = sub {
@@ -2959,6 +3111,7 @@ sub delete_null_alignments {
                 delete_selected_tokens( $rnew_lines->[$j], \@idel );
             }
         }
+        return;
     };
 
     foreach my $item ( @{$rsubgroups} ) {
@@ -4279,7 +4432,8 @@ sub is_good_side_comment_column {
     my $short_diff = SC_LONG_LINE_DIFF / ( 1 + $alev_diff * $num5 );
 
     goto FORGET
-      if ( $line_diff > $short_diff );
+      if ( $line_diff > $short_diff
+        || !$self->[_rOpts_valign_side_comments_] );
 
     # RULE3: Forget a side comment if this line is at lower level and
     # ends a block
@@ -4524,6 +4678,7 @@ sub valign_output_step_A {
     my $group_leader_length  = $rinput_hash->{group_leader_length};
     my $extra_leading_spaces = $rinput_hash->{extra_leading_spaces};
     my $level                = $rinput_hash->{level};
+    my $maximum_line_length  = $rinput_hash->{maximum_line_length};
 
     my $rfields                   = $line->get_rfields();
     my $rfield_lengths            = $line->get_rfield_lengths();
@@ -4607,6 +4762,7 @@ sub valign_output_step_A {
             level                     => $level,
             level_end                 => $level_end,
             Kend                      => $Kend,
+            maximum_line_length       => $maximum_line_length,
         }
     );
     return;
@@ -4670,12 +4826,14 @@ sub get_output_line_number {
     my $cached_line_text;
     my $cached_line_text_length;
     my $cached_line_type;
-    my $cached_line_flag;
+    my $cached_line_opening_flag;
+    my $cached_line_closing_flag;
     my $cached_seqno;
     my $cached_line_valid;
     my $cached_line_leading_space_count;
     my $cached_seqno_string;
     my $cached_line_Kend;
+    my $cached_line_maximum_length;
     my $seqno_string;
     my $last_nonblank_seqno_string;
 
@@ -4693,8 +4851,8 @@ sub get_output_line_number {
         return;
     }
 
-    sub get_cached_line_flag {
-        return $cached_line_flag;
+    sub get_cached_line_opening_flag {
+        return $cached_line_opening_flag;
     }
 
     sub get_cached_line_type {
@@ -4717,12 +4875,14 @@ sub get_output_line_number {
         $cached_line_text                = "";
         $cached_line_text_length         = 0;
         $cached_line_type                = 0;
-        $cached_line_flag                = 0;
+        $cached_line_opening_flag        = 0;
+        $cached_line_closing_flag        = 0;
         $cached_seqno                    = 0;
         $cached_line_valid               = 0;
         $cached_line_leading_space_count = 0;
         $cached_seqno_string             = "";
         $cached_line_Kend                = undef;
+        $cached_line_maximum_length      = undef;
 
         # These vars hold a string of sequence numbers joined together used by
         # the cache
@@ -4741,11 +4901,12 @@ sub get_output_line_number {
                 $self->[_last_level_written_],
                 $cached_line_Kend,
             );
-            $cached_line_type        = 0;
-            $cached_line_text        = "";
-            $cached_line_text_length = 0;
-            $cached_seqno_string     = "";
-            $cached_line_Kend        = undef;
+            $cached_line_type           = 0;
+            $cached_line_text           = "";
+            $cached_line_text_length    = 0;
+            $cached_seqno_string        = "";
+            $cached_line_Kend           = undef;
+            $cached_line_maximum_length = undef;
         }
         return;
     }
@@ -4770,6 +4931,7 @@ sub get_output_line_number {
         my $level                     = $rinput->{level};
         my $level_end                 = $rinput->{level_end};
         my $Kend                      = $rinput->{Kend};
+        my $maximum_line_length       = $rinput->{maximum_line_length};
 
         my $last_level_written = $self->[_last_level_written_];
 
@@ -4783,7 +4945,7 @@ sub get_output_line_number {
               $str_length -
               $side_comment_length +
               $leading_space_count -
-              $self->maximum_line_length_for_level($level);
+              $maximum_line_length;
             if ( $excess > 0 ) {
                 $leading_space_count = 0;
                 my $file_writer_object = $self->[_file_writer_object_];
@@ -4810,22 +4972,38 @@ sub get_output_line_number {
         my $leading_string_length = length($leading_string);
 
         # Unpack any recombination data; it was packed by
-        # sub send_lines_to_vertical_aligner. Contents:
+        # sub 'Formatter::set_vertical_tightness_flags'
+
+        # old   hash              Meaning
+        # index key
         #
-        #   [0] type: 1=opening non-block    2=closing non-block
-        #             3=opening block brace  4=closing block brace
-        #   [1] flag: if opening: 1=no multiple steps, 2=multiple steps ok
-        #             if closing: spaces of padding to use
-        #   [2] sequence number of container
-        #   [3] valid flag: do not append if this flag is false
+        # 0   _vt_type:           1=opening non-block    2=closing non-block
+        #                         3=opening block brace  4=closing block brace
         #
-        my ( $open_or_close, $tightness_flag, $seqno, $valid, $seqno_beg,
-            $seqno_end );
+        # 1a  _vt_opening_flag:  1=no multiple steps, 2=multiple steps ok
+        # 1b  _vt_closing_flag:    spaces of padding to use if closing
+        # 2   _vt_seqno:          sequence number of container
+        # 3   _vt_valid flag:     do not append if this flag is false. Will be
+        #           true if appropriate -vt flag is set.  Otherwise, Will be
+        #           made true only for 2 line container in parens with -lp
+        # 4   _vt_seqno_beg:      sequence number of first token of line
+        # 5   _vt_seqno_end:      sequence number of last token of line
+        # 6   _vt_min_lines:      min number of lines for joining opening cache,
+        #                           0=no constraint
+        # 7   _vt_max_lines:      max number of lines for joining opening cache,
+        #                           0=no constraint
+
+        my ( $open_or_close, $opening_flag, $closing_flag, $seqno, $valid,
+            $seqno_beg, $seqno_end );
         if ($rvertical_tightness_flags) {
-            (
-                $open_or_close, $tightness_flag, $seqno, $valid, $seqno_beg,
-                $seqno_end
-            ) = @{$rvertical_tightness_flags};
+
+            $open_or_close = $rvertical_tightness_flags->{_vt_type};
+            $opening_flag  = $rvertical_tightness_flags->{_vt_opening_flag};
+            $closing_flag  = $rvertical_tightness_flags->{_vt_closing_flag};
+            $seqno         = $rvertical_tightness_flags->{_vt_seqno};
+            $valid         = $rvertical_tightness_flags->{_vt_valid_flag};
+            $seqno_beg     = $rvertical_tightness_flags->{_vt_seqno_beg};
+            $seqno_end     = $rvertical_tightness_flags->{_vt_seqno_end};
         }
 
         $seqno_string = $seqno_end;
@@ -4852,7 +5030,7 @@ sub get_output_line_number {
                 my $gap = $leading_space_count - $cached_line_text_length;
 
                 # handle option of just one tight opening per line:
-                if ( $cached_line_flag == 1 ) {
+                if ( $cached_line_opening_flag == 1 ) {
                     if ( defined($open_or_close) && $open_or_close == 1 ) {
                         $gap = -1;
                     }
@@ -4865,27 +5043,24 @@ sub get_output_line_number {
                 # and breaks, causing -xci to alternately turn on and off (case
                 # b765).
                 # Patched to fix cases b656 b862 b971 b972: always do the check
-                # if -vmll is set.  The reason is that the -vmll option can
-                # cause changes in the maximum line length, leading to blinkers
-                # if not checked.
+                # if the maximum line length changes (due to -vmll).
                 if (
                     $gap >= 0
-                    && ( $self->[_rOpts_variable_maximum_line_length_]
+                    && ( $maximum_line_length != $cached_line_maximum_length
                         || ( defined($level_end) && $level > $level_end ) )
                   )
                 {
                     my $test_line_length =
                       $cached_line_text_length + $gap + $str_length;
-                    my $maximum_line_length =
-                      $self->maximum_line_length_for_level($last_level_written);
 
                     # Add a small tolerance in the length test (fixes case b862)
-                    if ( $test_line_length > $maximum_line_length - 2 ) {
+                    if ( $test_line_length > $cached_line_maximum_length - 2 ) {
                         $gap = -1;
                     }
                 }
 
                 if ( $gap >= 0 && defined($seqno_beg) ) {
+                    $maximum_line_length   = $cached_line_maximum_length;
                     $leading_string        = $cached_line_text . ' ' x $gap;
                     $leading_string_length = $cached_line_text_length + $gap;
                     $leading_space_count   = $cached_line_leading_space_count;
@@ -4903,9 +5078,11 @@ sub get_output_line_number {
             # Handle cached line ending in CLOSING tokens
             else {
                 my $test_line =
-                  $cached_line_text . ' ' x $cached_line_flag . $str;
+                  $cached_line_text . ' ' x $cached_line_closing_flag . $str;
                 my $test_line_length =
-                  $cached_line_text_length + $cached_line_flag + $str_length;
+                  $cached_line_text_length +
+                  $cached_line_closing_flag +
+                  $str_length;
                 if (
 
                     # The new line must start with container
@@ -4926,11 +5103,7 @@ sub get_output_line_number {
                     )
 
                     # The combined line must fit
-                    && (
-                        $test_line_length <=
-                        $self->maximum_line_length_for_level(
-                            $last_level_written)
-                    )
+                    && ( $test_line_length <= $cached_line_maximum_length )
                   )
                 {
 
@@ -5031,12 +5204,14 @@ sub get_output_line_number {
                         }
                     }
 
+                    # Change the args to look like we received the combined line
                     $str                   = $test_line;
                     $str_length            = $test_line_length;
                     $leading_string        = "";
                     $leading_string_length = 0;
                     $leading_space_count   = $cached_line_leading_space_count;
                     $level                 = $last_level_written;
+                    $maximum_line_length   = $cached_line_maximum_length;
                 }
                 else {
                     $self->valign_output_step_C(
@@ -5046,10 +5221,11 @@ sub get_output_line_number {
                 }
             }
         }
-        $cached_line_type        = 0;
-        $cached_line_text        = "";
-        $cached_line_text_length = 0;
-        $cached_line_Kend        = undef;
+        $cached_line_type           = 0;
+        $cached_line_text           = "";
+        $cached_line_text_length    = 0;
+        $cached_line_Kend           = undef;
+        $cached_line_maximum_length = undef;
 
         # make the line to be written
         my $line        = $leading_string . $str;
@@ -5078,12 +5254,14 @@ sub get_output_line_number {
             $cached_line_text                = $line;
             $cached_line_text_length         = $line_length;
             $cached_line_type                = $open_or_close;
-            $cached_line_flag                = $tightness_flag;
+            $cached_line_opening_flag        = $opening_flag;
+            $cached_line_closing_flag        = $closing_flag;
             $cached_seqno                    = $seqno;
             $cached_line_valid               = $valid;
             $cached_line_leading_space_count = $leading_space_count;
             $cached_seqno_string             = $seqno_string;
             $cached_line_Kend                = $Kend;
+            $cached_line_maximum_length      = $maximum_line_length;
         }
 
         $self->[_last_level_written_]       = $level;
@@ -5193,7 +5371,7 @@ sub get_output_line_number {
                 # Here is a complex example:
 
                 # Foo($Bar[0], {  # (side comment)
-                #      baz => 1,
+                #     baz => 1,
                 # });
 
                 # The first line has sequence 6::4.  It does not begin with
@@ -5274,7 +5452,7 @@ sub valign_output_step_D {
                 # shouldn't happen - program error counting whitespace
                 # - skip entabbing
                 DEBUG_TABS
-                  && $self->warning(
+                  && warning(
 "Error entabbing in valign_output_step_D: expected count=$leading_space_count\n"
                   );
             }
@@ -5292,7 +5470,7 @@ sub valign_output_step_D {
                 # But it could be an outdented comment
                 if ( $line !~ /^\s*#/ ) {
                     DEBUG_TABS
-                      && $self->warning(
+                      && warning(
 "Error entabbing in valign_output_step_D: for level=$level count=$leading_space_count\n"
                       );
                 }
@@ -5309,7 +5487,7 @@ sub valign_output_step_D {
                 # shouldn't happen - program error counting whitespace
                 # we'll skip entabbing
                 DEBUG_TABS
-                  && $self->warning(
+                  && warning(
 "Error entabbing in valign_output_step_D: expected count=$leading_space_count\n"
                   );
             }
@@ -5379,7 +5557,7 @@ sub valign_output_step_D {
             # shouldn't happen:
             if ( $space_count < 0 ) {
                 DEBUG_TABS
-                  && $self->warning(
+                  && warning(
 "Error in get_leading_string: for level=$group_level count=$leading_whitespace_count\n"
                   );
 
@@ -5393,7 +5571,7 @@ sub valign_output_step_D {
         $leading_string_cache[$leading_whitespace_count] = $leading_string;
         return $leading_string;
     }
-}    # end get_leading_string
+} ## end get_leading_string
 
 ##########################
 # CODE SECTION 10: Summary
@@ -5404,21 +5582,21 @@ sub report_anything_unusual {
 
     my $outdented_line_count = $self->[_outdented_line_count_];
     if ( $outdented_line_count > 0 ) {
-        $self->write_logfile_entry(
+        write_logfile_entry(
             "$outdented_line_count long lines were outdented:\n");
         my $first_outdented_line_at = $self->[_first_outdented_line_at_];
-        $self->write_logfile_entry(
+        write_logfile_entry(
             "  First at output line $first_outdented_line_at\n");
 
         if ( $outdented_line_count > 1 ) {
             my $last_outdented_line_at = $self->[_last_outdented_line_at_];
-            $self->write_logfile_entry(
+            write_logfile_entry(
                 "   Last at output line $last_outdented_line_at\n");
         }
-        $self->write_logfile_entry(
+        write_logfile_entry(
             "  use -noll to prevent outdenting, -l=n to increase line length\n"
         );
-        $self->write_logfile_entry("\n");
+        write_logfile_entry("\n");
     }
     return;
 }
index 79849c65de0cde8603b3320ab5708e716a372424..78b0b83e423609b2c98a7ace9f58578581da7553 100644 (file)
@@ -10,15 +10,20 @@ use warnings;
 
 { #<<< A non-indenting brace
 
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
-#    _column_          # the current column number
-#    _saved_column_    # a place for temporary storage
-my $i = 0;
-use constant {
-    _column_       => $i++,
-    _saved_column_ => $i++,
-};
+BEGIN {
+
+    # Indexes for variables in $self.
+    # Do not combine with other BEGIN blocks (c101).
+    #    _column_          # the current column number
+    #    _saved_column_    # a place for temporary storage
+    my $i = 0;
+    use constant {
+        _column_       => $i++,
+        _saved_column_ => $i++,
+    };
+}
 
 sub new {
     my ( $class, $rarg ) = @_;
index 72f9512a686dd12cea86332b9ebc937914647087..b5b0d1e35edc77e56508c8a181cec989eb5df5fd 100644 (file)
@@ -8,9 +8,12 @@
 package Perl::Tidy::VerticalAligner::Line;
 use strict;
 use warnings;
-our $VERSION = '20210717';
+our $VERSION = '20220217';
 
 BEGIN {
+
+    # Indexes for variables in $self.
+    # Do not combine with other BEGIN blocks (c101).
     my $i = 0;
     use constant {
         _jmax_                      => $i++,
@@ -143,6 +146,10 @@ EOM
         return $_[0]->[_is_hanging_side_comment_];
     }
 
+    sub get_maximum_line_length {
+        return $_[0]->[_maximum_line_length_];
+    }
+
     sub get_rvertical_tightness_flags {
         return $_[0]->[_rvertical_tightness_flags_];
     }
diff --git a/pm2pl b/pm2pl
index b62a6ef152f7384c5069294ceed089381bf4ad02..699293639bf78381ecd44e2bdc93eb4fe50adcd2 100755 (executable)
--- a/pm2pl
+++ b/pm2pl
@@ -1,19 +1,39 @@
 #!/usr/bin/env perl
 use strict;
+use Getopt::Long;
 
-# This script will recombine the perltidy binary script and all of its modules
-# into a single, monolithic script.  I use it for making a temporary "sandbox"
-# for debugging.
+my $usage = <<EOM;
 
-# This is also useful for making a copy of previous versions for parallel
-# debugging sessions.
+This script will recombine the perltidy binary script and all of its modules
+into a single, monolithic script.
 
-# usage:
-#   perl pm2pl
+Run this from the perltidy main installation directory.  It reads
+bin/perltidy and lib/*.pm and by default writes a file 'perltidy-VERSION.pl' in the
+current directory.
 
-# Run this from the perltidy main installation directory.  It reads
-# bin/perltidy and lib/*.pm and writes a file 'perltidy-VERSION.pl' in the
-# current directory.
+usage:
+  perl pm2pl [-h -o ofile ]
+
+  -h prints this help
+  -o set output file      [ default is 'perltidy-VERSION.pl' ]
+  -D set DEVEL_MODE => 1  [ for extra testing when debugging ]
+EOM
+
+my @option_string = qw(
+  h
+  o:s
+  D
+);
+
+my %Opts = ();
+if ( !GetOptions( \%Opts, @option_string ) ) {
+    die "Programming Bug: error in setting default options";
+}
+
+if ( $Opts{h} ) {
+    print $usage;
+    exit 1;
+}
 
 # This should work for a system with File::Spec,
 # and for older Windows/Unix systems without File::Spec.
@@ -51,44 +71,59 @@ unless ($missing_file_spec) {
 
 my $VERSION = get_version("lib/Perl/Tidy.pm");
 my $outfile = "perltidy-$VERSION.pl";
-open OUTFILE, "> $outfile" or die "can't open file '$outfile' : $!\n";
-print "Creating standalone formatter script '$outfile' ....\n ";
+if ( $Opts{o} ) { $outfile = $Opts{o} }
+my $fh_out;
+open( $fh_out,, ">", $outfile ) or die "can't open file '$outfile' : $!\n";
+print "Creating standalone perltidy script '$outfile' ....";
 
 # first, open the script and copy the first (hash-bang) line
 # (Note: forward slashes in file names here will work in Windows)
-open SCRIPT, "< $script" or die "can't open script file '$script' : $!\n";
-my $hash_bang = <SCRIPT>;
-print OUTFILE $hash_bang;
+my $fh_in;
+open( $fh_in, "<", $script ) or die "can't open script file '$script' : $!\n";
+my $hash_bang = <$fh_in>;
+$fh_out->print($hash_bang);
 
 # then copy all modules
+my $changed_count;
 foreach my $module (@modules) {
-    open PM, "< $module" or die "can't open my module file '$module' : $!\n";
-    while (<PM>) {
+    my $fh_module;
+    open( $fh_module, '<', $module )
+      or die "can't open my module file '$module' : $!\n";
+    while (<$fh_module>) {
         last if /^\s*__END__\s*$/;
-        print OUTFILE unless $_ =~ /^use Perl::Tidy/;
+        my $line = $_;
+        if (   $Opts{'D'}
+            && $line =~
+            /^(\s*use\s+constant\s+(?:DEVEL)_[A-Z]+\s*)=>\s*(-?\d*);(.*)$/ )
+        {
+            if ( $2 != '1' ) {
+                $changed_count++;
+                $line = <<EOM;
+$1=> 1;$3
+EOM
+            }
+        }
+
+        $fh_out->print($line) unless $line =~ /^use Perl::Tidy/;
     }
-    close PM;
+    $fh_module->close();
 }
 
 # then, copy the rest of the script except for the 'use PerlTidy' statement
-while (<SCRIPT>) {
+while (<$fh_in>) {
     last if /^\s*__END__\s*$/;
-    print OUTFILE unless $_ =~ /^use Perl::Tidy/;
+    $fh_out->print($_) unless $_ =~ /^use Perl::Tidy/;
 }
-close SCRIPT;
-close OUTFILE;
+$fh_in->close();
+$fh_out->close();
 chmod 0755, $outfile;
 
-my $testfile = "somefile.pl";
-print <<EOM;
-
-You can now run $outfile to reformat perl scripts.  
-For example, the following command:
-
-    perl $outfile $testfile
-
-will produce the output file $testfile.tdy
+print " OK\n";
+if ($changed_count) {
+    print <<EOM;
+$changed_count lines changed to DEVEL_MODE => 1
 EOM
+}
 
 sub get_version {
     my ($file) = @_;
diff --git a/t/.gitattributes b/t/.gitattributes
new file mode 100644 (file)
index 0000000..fa1385d
--- /dev/null
@@ -0,0 +1 @@
+* -text
index e726bd4d086d533da5ff97c099c5df719e7c13c9..033da5f9b17710b1c642118a154f085cbcabdb88 100644 (file)
@@ -76,10 +76,27 @@ BEGIN {
     $rsources = {
 
         'align32' => <<'----------',
-# should not get alignment here:
+# align just the last two lines
 my $c_sub_khwnd = WindowFromId $k_hwnd, 0x8008;    # FID_CLIENT
 ok $c_sub_khwnd, 'have kids client window';
 ok IsWindow($c_sub_khwnd), 'IsWindow works on the client';
+
+# parenless calls
+mkTextConfig $c, $x, $y, -anchor => 'se', $color;
+mkTextConfig $c, $x + 30, $y, -anchor => 's',  $color;
+mkTextConfig $c, $x + 60, $y, -anchor => 'sw', $color;
+mkTextConfig $c, $x, $y + 30, -anchor => 'e', $color;
+
+permute_test [ 'a', 'b', 'c' ],   '/', '/', [ 'a', 'b', 'c' ];
+permute_test [ 'a,', 'b', 'c,' ], '/', '/', [ 'a,', 'b', 'c,' ];
+permute_test [ 'a', ',', '#', 'c' ], '/', '/', [ 'a', ',', '#', 'c' ];
+permute_test [ 'f_oo', 'b_ar' ], '/', '/', [ 'f_oo', 'b_ar' ];
+
+# issue c093 - broken sub, but align fat commas
+use constant UNDEF_ONLY => sub { not defined $_[0] };
+use constant EMPTY_OR_UNDEF => sub {
+    !@_ or @_ == 1 && !defined $_[0];
+};
 ----------
 
         'bos' => <<'----------',
@@ -314,10 +331,27 @@ sub plugh () : Ugly('\(") : Bad;
             source => "align32",
             params => "def",
             expect => <<'#1...........',
-# should not get alignment here:
+# align just the last two lines
 my $c_sub_khwnd = WindowFromId $k_hwnd, 0x8008;    # FID_CLIENT
-ok $c_sub_khwnd, 'have kids client window';
+ok $c_sub_khwnd,           'have kids client window';
 ok IsWindow($c_sub_khwnd), 'IsWindow works on the client';
+
+# parenless calls
+mkTextConfig $c, $x,      $y,      -anchor => 'se', $color;
+mkTextConfig $c, $x + 30, $y,      -anchor => 's',  $color;
+mkTextConfig $c, $x + 60, $y,      -anchor => 'sw', $color;
+mkTextConfig $c, $x,      $y + 30, -anchor => 'e',  $color;
+
+permute_test [ 'a', 'b', 'c' ],      '/', '/', [ 'a', 'b', 'c' ];
+permute_test [ 'a,', 'b', 'c,' ],    '/', '/', [ 'a,', 'b', 'c,' ];
+permute_test [ 'a', ',', '#', 'c' ], '/', '/', [ 'a', ',', '#', 'c' ];
+permute_test [ 'f_oo', 'b_ar' ],     '/', '/', [ 'f_oo', 'b_ar' ];
+
+# issue c093 - broken sub, but align fat commas
+use constant UNDEF_ONLY     => sub { not defined $_[0] };
+use constant EMPTY_OR_UNDEF => sub {
+    !@_ or @_ == 1 && !defined $_[0];
+};
 #1...........
         },
 
index 67ca2647ab45b9fc710d1843759491ded97a8272..2dec3547ce344baf570d0c6004e5a76eda4bc69d 100644 (file)
@@ -609,8 +609,7 @@ Mojo::IOLoop->next_tick(
 );
 
 $r = do
-  { sswitch( $words[ rand @words ] )
-    {
+  { sswitch( $words[ rand @words ] ) {
         case $words[0]:
         case $words[1]:
         case $words[2]:
@@ -619,12 +618,10 @@ $r = do
     }
   };
 
-try
-{
+try {
     die;
 }
-catch
-{
+catch {
     die;
 };
 #7...........
index 5740606fcd4dd029ac14e0b51a9b7abf6f0f18c2..753c55216e9eea9acc64b00860caa363bedacf3b 100644 (file)
@@ -435,14 +435,12 @@ my ( $v1, $v2 ) = @_;     # test -sak='push'
             source => "braces",
             params => "braces5",
             expect => <<'#17...........',
-sub message
-{
+sub message {
     if ( !defined( $_[0] ) )
       {
         print("Hello, World\n");
       }
-    else
-    {
+    else {
         print( $_[0], "\n" );
     }
 }
@@ -454,8 +452,7 @@ $myfun = sub {
 eval {
     my $app = App::perlbrew->new( "install-patchperl", "-q" );
     $app->run();
-} or do
-{
+} or do {
     $error          = $@;
     $produced_error = 1;
 };
@@ -471,10 +468,8 @@ Mojo::IOLoop->next_tick(
     }
 );
 
-$r = do
-{
-    sswitch( $words[ rand @words ] )
-    {
+$r = do {
+    sswitch( $words[ rand @words ] ) {
         case $words[0]:
         case $words[1]:
         case $words[2]:
@@ -483,12 +478,10 @@ $r = do
     }
 };
 
-try
-{
+try {
     die;
 }
-catch
-{
+catch {
     die;
 };
 #17...........
index 58b30747a9a52664602b9c5385c5cb1ff1ee2683..d5a8073015e4b08761d3b62fffce082219e16c00 100644 (file)
@@ -104,7 +104,13 @@ my $mapping = [
 ----------
 
         'gnu6' => <<'----------',
-# the closing braces should have the same position for these two hashes with -gnu
+# These closing braces no longer have the same position with -gnu after an
+# update 13 dec 2021 in which the vertical aligner zeros recoverable spaces.
+# But adding the -xlp should make them all have the same indentation.
+    $var1 = {
+        'foo10' => undef,
+        'foo72' => ' ',
+    };
     $var1 = {
         'foo10' => undef,
         'foo72' => '
@@ -115,7 +121,6 @@ my $mapping = [
 ',
         'foo10' => undef,
     };
-
 ----------
 
         'hanging_side_comments3' => <<'----------',
@@ -409,7 +414,13 @@ else {
             source => "gnu6",
             params => "def",
             expect => <<'#9...........',
-# the closing braces should have the same position for these two hashes with -gnu
+    # These closing braces no longer have the same position with -gnu after an
+    # update 13 dec 2021 in which the vertical aligner zeros recoverable spaces.
+    # But adding the -xlp should make them all have the same indentation.
+    $var1 = {
+        'foo10' => undef,
+        'foo72' => ' ',
+    };
     $var1 = {
         'foo10' => undef,
         'foo72' => '
@@ -420,7 +431,6 @@ else {
 ',
         'foo10' => undef,
     };
-
 #9...........
         },
 
@@ -428,18 +438,23 @@ else {
             source => "gnu6",
             params => "gnu",
             expect => <<'#10...........',
-    # the closing braces should have the same position for these two hashes with -gnu
+    # These closing braces no longer have the same position with -gnu after an
+    # update 13 dec 2021 in which the vertical aligner zeros recoverable spaces.
+    # But adding the -xlp should make them all have the same indentation.
+    $var1 = {
+             'foo10' => undef,
+             'foo72' => ' ',
+            };
     $var1 = {
         'foo10' => undef,
         'foo72' => '
 ',
-            };
+    };
     $var2 = {
         'foo72' => '
 ',
         'foo10' => undef,
             };
-
 #10...........
         },
 
index 92563c258b62f8068a3886aed94ba6591889750b..a86591f28c8122bbafb02b51d742abeff3fa3fc8 100644 (file)
@@ -46,7 +46,7 @@ BEGIN {
 -nboa
 ----------
         'braces7' => <<'----------',
--bli -blil='*'
+-bli -blil='*' -blixl='eval'
 ----------
         'def'       => "",
         'extrude'   => "--extrude",
@@ -369,11 +369,10 @@ $myfun = sub
     print("Hello, World\n");
   };
 
-eval
-  {
+eval {
     my $app = App::perlbrew->new( "install-patchperl", "-q" );
     $app->run();
-  } or do
+} or do
   {
     $error          = $@;
     $produced_error = 1;
index aa02151e4db9d301f45c0bf8e1b2346afc31c53d..3a783c551ca1cd119b0db3615deda94c58846bd8 100644 (file)
@@ -17,6 +17,9 @@
 #14 rt136417.rt136417
 #15 numbers.def
 #16 code_skipping.def
+#17 git51.def
+#18 git51.git51
+#19 pretok.def
 
 # To locate test #13 you can search for its name or the string '#13'
 
@@ -36,7 +39,29 @@ BEGIN {
     $rparams = {
         'def'   => "",
         'fpva1' => "-sfp",
-        'fpva2' => "-sfp -nfpva",
+        'fpva2' => <<'----------',
+-sfp -wls='->' -wrs='->' -nfpva
+----------
+        'git51' => <<'----------',
+--maximum-line-length=120
+--converge
+--tabs
+--entab-leading-whitespace=4
+--continuation-indentation=4
+--extended-continuation-indentation
+--no-delete-old-newlines
+--no-outdent-long-lines
+--no-outdent-labels
+--novalign
+--no-logical-padding
+--opening-sub-brace-on-new-line
+--square-bracket-tightness=2
+--paren-tightness=2
+--brace-tightness=2
+--opening-token-right
+
+-sal='first any sum sum0 reduce'
+----------
         'git54' => "-bbp=3 -bbpi=2 -ci=4 -lp",
         'lpxl1' => "-lp",
         'lpxl3' => <<'----------',
@@ -96,6 +121,18 @@ Coro::AnyEvent::sleep( 3, 4 );
 use Carp ();
 use File::Spec ();
 use File::Path ();
+$self -> method ( 'parameter_0', 'parameter_1' );
+$self -> method_with_long_name ( 'parameter_0', 'parameter_1' );
+----------
+
+        'git51' => <<'----------',
+Type::Libraries->setup_class(
+       __PACKAGE__,
+       qw(
+               Types::Standard
+               Types::Common::Numeric
+               ), # <--- brace here
+);
 ----------
 
         'git54' => <<'----------',
@@ -246,6 +283,20 @@ my @vals = (
     0o12_345,  # optional 'o' and 'O' added in perl v5.33.5
     0O12_345,
 );
+----------
+
+        'pretok' => <<'----------',
+# test sub split_pretoken
+my$s1=$^??"def":"not def";
+my$s2=$^ ?"def":"not def";
+my$s3=$^if($s2);
+my$s4=$^Oeq"linux";
+my$s5=$  ^One"linux";
+my$s6=$
+  ^One"linux";
+my$s7=%^O;
+my$s8='hi'.'s'x10if(1);
+my$s9='merci'x0.1e4.$s8;
 ----------
 
         'rt136417' => <<'----------',
@@ -388,6 +439,8 @@ Coro::AnyEvent::sleep( 3, 4 );
 use Carp       ();
 use File::Spec ();
 use File::Path ();
+$self->method( 'parameter_0', 'parameter_1' );
+$self->method_with_long_name( 'parameter_0', 'parameter_1' );
 #3...........
         },
 
@@ -400,6 +453,8 @@ Coro::AnyEvent::sleep            ( 3, 4 );
 use Carp       ();
 use File::Spec ();
 use File::Path ();
+$self->method                ( 'parameter_0', 'parameter_1' );
+$self->method_with_long_name ( 'parameter_0', 'parameter_1' );
 #4...........
         },
 
@@ -412,6 +467,8 @@ Coro::AnyEvent::sleep ( 3, 4 );
 use Carp ();
 use File::Spec ();
 use File::Path ();
+$self -> method ( 'parameter_0', 'parameter_1' );
+$self -> method_with_long_name ( 'parameter_0', 'parameter_1' );
 #5...........
         },
 
@@ -580,11 +637,11 @@ debug(
 # simple function call with code block
 $m->command(
     -label   => 'Save',
-    -command => sub { print "DOS\n"; save_dialog($win); } );
+    -command => sub { print "DOS\n"; save_dialog($win); }
+);
 
 # function call, ternary in list
-return
-  OptArgs2::Result->usage(
+return OptArgs2::Result->usage(
     $style == OptArgs2::STYLE_FULL ? 'FullUsage' : 'NormalUsage',
     'usage: ' . $usage . "\n" );
 
@@ -715,11 +772,11 @@ debug(
 # simple function call with code block
 $m->command(
     -label   => 'Save',
-    -command => sub { print "DOS\n"; save_dialog($win); } );
+    -command => sub { print "DOS\n"; save_dialog($win); }
+);
 
 # function call, ternary in list
-return
-  OptArgs2::Result->usage(
+return OptArgs2::Result->usage(
     $style == OptArgs2::STYLE_FULL ? 'FullUsage' : 'NormalUsage',
     'usage: ' . $usage . "\n" );
 
@@ -864,6 +921,51 @@ my $self    = shift;
 my $cloning = shift;
 #16...........
         },
+
+        'git51.def' => {
+            source => "git51",
+            params => "def",
+            expect => <<'#17...........',
+Type::Libraries->setup_class(
+    __PACKAGE__,
+    qw(
+      Types::Standard
+      Types::Common::Numeric
+    ),    # <--- brace here
+);
+#17...........
+        },
+
+        'git51.git51' => {
+            source => "git51",
+            params => "git51",
+            expect => <<'#18...........',
+Type::Libraries->setup_class(
+       __PACKAGE__,
+       qw(
+               Types::Standard
+               Types::Common::Numeric
+       ),    # <--- brace here
+);
+#18...........
+        },
+
+        'pretok.def' => {
+            source => "pretok",
+            params => "def",
+            expect => <<'#19...........',
+# test sub split_pretoken
+my $s1 = $^? ? "def" : "not def";
+my $s2 = $^  ? "def" : "not def";
+my $s3 = $^ if ($s2);
+my $s4 = $^O eq "linux";
+my $s5 = $^O ne "linux";
+my $s6 = $^O ne "linux";
+my $s7 = %^O;
+my $s8 = 'hi' . 's' x 10 if (1);
+my $s9 = 'merci' x 0.1e4 . $s8;
+#19...........
+        },
     };
 
     my $ntests = 0 + keys %{$rtests};
diff --git a/t/snippets25.t b/t/snippets25.t
new file mode 100644 (file)
index 0000000..83e2873
--- /dev/null
@@ -0,0 +1,845 @@
+# Created with: ./make_t.pl
+
+# Contents:
+#1 novalign.def
+#2 novalign.novalign1
+#3 novalign.novalign2
+#4 novalign.novalign3
+#5 lp2.def
+#6 lp2.lp
+#7 braces.braces8
+#8 rt140025.def
+#9 rt140025.rt140025
+#10 xlp1.def
+#11 xlp1.xlp1
+#12 git74.def
+#13 git74.git74
+#14 git77.def
+#15 git77.git77
+#16 vxl.def
+#17 vxl.vxl1
+#18 vxl.vxl2
+#19 bal.bal1
+
+# To locate test #13 you can search for its name or the string '#13'
+
+use strict;
+use Test::More;
+use Carp;
+use Perl::Tidy;
+my $rparams;
+my $rsources;
+my $rtests;
+
+BEGIN {
+
+    ###########################################
+    # BEGIN SECTION 1: Parameter combinations #
+    ###########################################
+    $rparams = {
+        'bal1'    => "-bal=1",
+        'braces8' => <<'----------',
+-bl -bbvt=1 -blxl=' ' -bll='sub do asub'
+----------
+        'def'   => "",
+        'git74' => <<'----------',
+-xlp
+--iterations=2
+--maximum-line-length=120
+--line-up-parentheses
+--continuation-indentation=4
+--closing-token-indentation=1
+--want-left-space="= -> ( )"
+--want-right-space="= -> ( )"
+--space-function-paren
+--space-keyword-paren
+--space-terminal-semicolon
+--opening-brace-on-new-line
+--opening-sub-brace-on-new-line
+--opening-anonymous-sub-brace-on-new-line
+--brace-left-and-indent
+--brace-left-and-indent-list="*"
+--break-before-hash-brace=3
+----------
+        'git77' => <<'----------',
+-gal='Grep Map'
+----------
+        'lp'        => "-lp",
+        'novalign1' => "-novalign",
+        'novalign2' => "-nvsc -nvbc -msc=2",
+        'novalign3' => "-nvc",
+        'rt140025'  => "-lp -xci -ci=4 -ce",
+        'vxl1'      => <<'----------',
+-vxl='='
+----------
+        'vxl2' => <<'----------',
+-vxl='*' -vil='='
+----------
+        'xlp1' => "-xlp",
+    };
+
+    ############################
+    # BEGIN SECTION 2: Sources #
+    ############################
+    $rsources = {
+
+        'bal' => <<'----------',
+{
+  L1:
+  L2:
+  L3: return;
+};
+----------
+
+        'braces' => <<'----------',
+sub message {
+    if ( !defined( $_[0] ) ) {
+        print("Hello, World\n");
+    }
+    else {
+        print( $_[0], "\n" );
+    }
+}
+
+$myfun = sub {
+    print("Hello, World\n");
+};
+
+eval {
+    my $app = App::perlbrew->new( "install-patchperl", "-q" );
+    $app->run();
+} or do {
+    $error          = $@;
+    $produced_error = 1;
+};
+
+Mojo::IOLoop->next_tick(
+    sub {
+        $ua->get(
+            '/' => sub {
+                push @kept_alive, pop->kept_alive;
+                Mojo::IOLoop->next_tick( sub { Mojo::IOLoop->stop } );
+            }
+        );
+    }
+);
+
+$r = do {
+    sswitch( $words[ rand @words ] ) {
+        case $words[0]:
+        case $words[1]:
+        case $words[2]:
+        case $words[3]: { 'ok' }
+      default: { 'wtf' }
+    }
+};
+
+try {
+    die;
+}
+catch {
+    die;
+};
+----------
+
+        'git74' => <<'----------',
+$self->func(
+  {
+    command  => [ 'command', 'argument1', 'argument2' ],
+    callback => sub {
+      my ($res) = @_;
+      print($res);
+    }
+  }
+);
+
+my $test_var = $self->test_call(    #
+    $arg1,
+    $arg2
+);
+
+my $test_var = $self->test_call(
+    $arg1,                          #
+    $arg2
+);
+
+my $test_var = $self->test_call(
+    #
+    $arg1,
+    $arg2,
+);
+
+my $test_var = $self->test_call(
+
+    $arg1,
+    $arg2,
+);
+
+my $test_var = $self->test_call(
+    $arg1,
+    $arg2
+
+);
+
+my $test_var = $self->test_call(
+
+    $arg1,
+    $arg2,
+
+);
+
+my $test_var =
+
+  $self->test_call(
+    $arg1,
+    $arg2
+
+  );
+
+----------
+
+        'git77' => <<'----------',
+# These should format about the same with -gal='Map Grep'.
+# NOTE: The braces only align if the internal code flag ALIGN_GREP_ALIASES is set
+    return +{
+        Map  {
+$_->init_arg => $_->get_value($instance) }
+        Grep { $_->has_value($instance) }
+        Grep {
+defined( $_->init_arg ) }
+$class->get_all_attributes
+    };
+
+    return +{
+        map  {
+$_->init_arg => $_->get_value($instance) }
+        grep { $_->has_value($instance) }
+        grep {
+defined( $_->init_arg ) }
+$class->get_all_attributes
+    };
+----------
+
+        'lp2' => <<'----------',
+# test issue git #74, lost -lp when final anon sub brace followed by '}'
+Util::Parser->new(
+    Handlers => {
+        Init  => sub { $self->init(@_) },
+        Mid =>  { sub { shift; $self->mid(@_) } },
+        Final => sub { shift; $self->final(@_) }
+    }
+)->parse( $_[0] );
+----------
+
+        'novalign' => <<'----------',
+{
+# simple vertical alignment of '=' and '#'
+# A long line to test -nvbc ... normally this will cause the previous line to move left
+my $lines = 0;    # checksum: #lines
+my $bytes = 0;    # checksum: #bytes
+my $sum = 0;    # checksum: system V sum
+my $patchdata = 0;    # saw patch data
+my $pos = 0;    # start of patch data
+                                         # a hanging side comment
+my $endkit = 0;    # saw end of kit
+my $fail = 0;    # failed
+}
+
+----------
+
+        'rt140025' => <<'----------',
+eval {
+my $cpid;
+my $cmd;
+
+ FORK: {
+ if( $cpid = fork ) {
+ close( STDOUT );
+ last;
+ } elsif( defined $cpid ) {
+ close( STDIN );
+ open( STDIN, '<', '/dev/null' ) or die( "open3: $!\n" );
+ exec $cmd or die( "exec: $!\n" );
+ } elsif( $! == EAGAIN ) {
+ sleep 3;
+ redo FORK;
+ } else {
+ die( "Can't fork: $!\n" );
+ }
+ }
+};
+----------
+
+        'vxl' => <<'----------',
+# if equals is excluded then ternary is automatically excluded
+# side comment alignments always remain
+$co_description = ($color) ? 'bold cyan' : '';          # description
+$co_prompt      = ($color) ? 'bold green' : '';         # prompt
+$co_unused      = ($color) ? 'on_green' : 'reverse';    # unused
+----------
+
+        'xlp1' => <<'----------',
+# test -xlp with comments, broken sub blocks, blank line, line length limit
+$cb1 = $act_page->Checkbutton(
+  -text     => M "Verwenden",
+  -variable => \$qualitaet_s_optimierung,
+  -command  => sub {
+    change_state_all( $act_page1, $qualitaet_s_optimierung, { $cb1 => 1 } )
+      ;    # sc
+  },
+)->grid(
+
+  # block comment
+  -row    => $gridy++,
+  -column => 2,
+  -sticky => 'e'
+);
+----------
+    };
+
+    ####################################
+    # BEGIN SECTION 3: Expected output #
+    ####################################
+    $rtests = {
+
+        'novalign.def' => {
+            source => "novalign",
+            params => "def",
+            expect => <<'#1...........',
+{
+# simple vertical alignment of '=' and '#'
+# A long line to test -nvbc ... normally this will cause the previous line to move left
+    my $lines     = 0;    # checksum: #lines
+    my $bytes     = 0;    # checksum: #bytes
+    my $sum       = 0;    # checksum: system V sum
+    my $patchdata = 0;    # saw patch data
+    my $pos       = 0;    # start of patch data
+                          # a hanging side comment
+    my $endkit    = 0;    # saw end of kit
+    my $fail      = 0;    # failed
+}
+
+#1...........
+        },
+
+        'novalign.novalign1' => {
+            source => "novalign",
+            params => "novalign1",
+            expect => <<'#2...........',
+{
+    # simple vertical alignment of '=' and '#'
+# A long line to test -nvbc ... normally this will cause the previous line to move left
+    my $lines = 0;    # checksum: #lines
+    my $bytes = 0;    # checksum: #bytes
+    my $sum = 0;    # checksum: system V sum
+    my $patchdata = 0;    # saw patch data
+    my $pos = 0;    # start of patch data
+                    # a hanging side comment
+    my $endkit = 0;    # saw end of kit
+    my $fail = 0;    # failed
+}
+
+#2...........
+        },
+
+        'novalign.novalign2' => {
+            source => "novalign",
+            params => "novalign2",
+            expect => <<'#3...........',
+{
+    # simple vertical alignment of '=' and '#'
+# A long line to test -nvbc ... normally this will cause the previous line to move left
+    my $lines     = 0;  # checksum: #lines
+    my $bytes     = 0;  # checksum: #bytes
+    my $sum       = 0;  # checksum: system V sum
+    my $patchdata = 0;  # saw patch data
+    my $pos       = 0;  # start of patch data
+      # a hanging side comment
+    my $endkit = 0;  # saw end of kit
+    my $fail = 0;  # failed
+}
+
+#3...........
+        },
+
+        'novalign.novalign3' => {
+            source => "novalign",
+            params => "novalign3",
+            expect => <<'#4...........',
+{
+# simple vertical alignment of '=' and '#'
+# A long line to test -nvbc ... normally this will cause the previous line to move left
+    my $lines = 0;        # checksum: #lines
+    my $bytes = 0;        # checksum: #bytes
+    my $sum = 0;          # checksum: system V sum
+    my $patchdata = 0;    # saw patch data
+    my $pos = 0;          # start of patch data
+                          # a hanging side comment
+    my $endkit = 0;       # saw end of kit
+    my $fail = 0;         # failed
+}
+
+#4...........
+        },
+
+        'lp2.def' => {
+            source => "lp2",
+            params => "def",
+            expect => <<'#5...........',
+# test issue git #74, lost -lp when final anon sub brace followed by '}'
+Util::Parser->new(
+    Handlers => {
+        Init  => sub { $self->init(@_) },
+        Mid   => { sub { shift; $self->mid(@_) } },
+        Final => sub { shift; $self->final(@_) }
+    }
+)->parse( $_[0] );
+#5...........
+        },
+
+        'lp2.lp' => {
+            source => "lp2",
+            params => "lp",
+            expect => <<'#6...........',
+# test issue git #74, lost -lp when final anon sub brace followed by '}'
+Util::Parser->new(
+                   Handlers => {
+                                 Init  => sub { $self->init(@_) },
+                                 Mid   => { sub { shift; $self->mid(@_) } },
+                                 Final => sub { shift; $self->final(@_) }
+                   }
+)->parse( $_[0] );
+#6...........
+        },
+
+        'braces.braces8' => {
+            source => "braces",
+            params => "braces8",
+            expect => <<'#7...........',
+sub message
+{   if ( !defined( $_[0] ) ) {
+        print("Hello, World\n");
+    }
+    else {
+        print( $_[0], "\n" );
+    }
+}
+
+$myfun = sub
+{   print("Hello, World\n");
+};
+
+eval {
+    my $app = App::perlbrew->new( "install-patchperl", "-q" );
+    $app->run();
+} or do
+{   $error          = $@;
+    $produced_error = 1;
+};
+
+Mojo::IOLoop->next_tick(
+    sub
+    {   $ua->get(
+            '/' => sub
+            {   push @kept_alive, pop->kept_alive;
+                Mojo::IOLoop->next_tick( sub { Mojo::IOLoop->stop } );
+            }
+        );
+    }
+);
+
+$r = do
+{   sswitch( $words[ rand @words ] ) {
+        case $words[0]:
+        case $words[1]:
+        case $words[2]:
+        case $words[3]: { 'ok' }
+      default: { 'wtf' }
+    }
+};
+
+try {
+    die;
+}
+catch {
+    die;
+};
+#7...........
+        },
+
+        'rt140025.def' => {
+            source => "rt140025",
+            params => "def",
+            expect => <<'#8...........',
+eval {
+    my $cpid;
+    my $cmd;
+
+  FORK: {
+        if ( $cpid = fork ) {
+            close(STDOUT);
+            last;
+        }
+        elsif ( defined $cpid ) {
+            close(STDIN);
+            open( STDIN, '<', '/dev/null' ) or die("open3: $!\n");
+            exec $cmd                       or die("exec: $!\n");
+        }
+        elsif ( $! == EAGAIN ) {
+            sleep 3;
+            redo FORK;
+        }
+        else {
+            die("Can't fork: $!\n");
+        }
+    }
+};
+#8...........
+        },
+
+        'rt140025.rt140025' => {
+            source => "rt140025",
+            params => "rt140025",
+            expect => <<'#9...........',
+eval {
+    my $cpid;
+    my $cmd;
+
+FORK: {
+        if ( $cpid = fork ) {
+            close(STDOUT);
+            last;
+        } elsif ( defined $cpid ) {
+            close(STDIN);
+            open( STDIN, '<', '/dev/null' ) or die("open3: $!\n");
+            exec $cmd                       or die("exec: $!\n");
+        } elsif ( $! == EAGAIN ) {
+            sleep 3;
+            redo FORK;
+        } else {
+            die("Can't fork: $!\n");
+        }
+    }
+};
+#9...........
+        },
+
+        'xlp1.def' => {
+            source => "xlp1",
+            params => "def",
+            expect => <<'#10...........',
+# test -xlp with comments, broken sub blocks, blank line, line length limit
+$cb1 = $act_page->Checkbutton(
+    -text     => M "Verwenden",
+    -variable => \$qualitaet_s_optimierung,
+    -command  => sub {
+        change_state_all( $act_page1, $qualitaet_s_optimierung, { $cb1 => 1 } )
+          ;    # sc
+    },
+)->grid(
+
+    # block comment
+    -row    => $gridy++,
+    -column => 2,
+    -sticky => 'e'
+);
+#10...........
+        },
+
+        'xlp1.xlp1' => {
+            source => "xlp1",
+            params => "xlp1",
+            expect => <<'#11...........',
+# test -xlp with comments, broken sub blocks, blank line, line length limit
+$cb1 = $act_page->Checkbutton(
+                              -text     => M "Verwenden",
+                              -variable => \$qualitaet_s_optimierung,
+                              -command  => sub {
+                                  change_state_all( $act_page1,
+                                       $qualitaet_s_optimierung, { $cb1 => 1 } )
+                                    ;    # sc
+                              },
+)->grid(
+
+        # block comment
+        -row    => $gridy++,
+        -column => 2,
+        -sticky => 'e'
+);
+#11...........
+        },
+
+        'git74.def' => {
+            source => "git74",
+            params => "def",
+            expect => <<'#12...........',
+$self->func(
+    {
+        command  => [ 'command', 'argument1', 'argument2' ],
+        callback => sub {
+            my ($res) = @_;
+            print($res);
+        }
+    }
+);
+
+my $test_var = $self->test_call(    #
+    $arg1,
+    $arg2
+);
+
+my $test_var = $self->test_call(
+    $arg1,                          #
+    $arg2
+);
+
+my $test_var = $self->test_call(
+    #
+    $arg1,
+    $arg2,
+);
+
+my $test_var = $self->test_call(
+
+    $arg1,
+    $arg2,
+);
+
+my $test_var = $self->test_call(
+    $arg1,
+    $arg2
+
+);
+
+my $test_var = $self->test_call(
+
+    $arg1,
+    $arg2,
+
+);
+
+my $test_var =
+
+  $self->test_call(
+    $arg1,
+    $arg2
+
+  );
+
+#12...........
+        },
+
+        'git74.git74' => {
+            source => "git74",
+            params => "git74",
+            expect => <<'#13...........',
+$self -> func (
+                {
+                   command  => [ 'command', 'argument1', 'argument2' ],
+                   callback => sub
+                       {
+                       my ($res) = @_ ;
+                       print ($res) ;
+                       }
+                }
+              ) ;
+
+my $test_var = $self -> test_call (    #
+                                    $arg1,
+                                    $arg2
+                                  ) ;
+
+my $test_var = $self -> test_call (
+                                    $arg1,    #
+                                    $arg2
+                                  ) ;
+
+my $test_var = $self -> test_call (
+                                   #
+                                   $arg1,
+                                   $arg2,
+                                  ) ;
+
+my $test_var = $self -> test_call (
+
+                                   $arg1,
+                                   $arg2,
+                                  ) ;
+
+my $test_var = $self -> test_call (
+                                    $arg1,
+                                    $arg2
+
+                                  ) ;
+
+my $test_var = $self -> test_call (
+
+                                   $arg1,
+                                   $arg2,
+
+                                  ) ;
+
+my $test_var =
+
+    $self -> test_call (
+                         $arg1,
+                         $arg2
+
+                       ) ;
+
+#13...........
+        },
+
+        'git77.def' => {
+            source => "git77",
+            params => "def",
+            expect => <<'#14...........',
+# These should format about the same with -gal='Map Grep'.
+# NOTE: The braces only align if the internal code flag ALIGN_GREP_ALIASES is set
+    return +{
+        Map {
+            $_->init_arg => $_->get_value($instance)
+        } Grep { $_->has_value($instance) }
+        Grep {
+            defined( $_->init_arg )
+        }
+        $class->get_all_attributes
+    };
+
+    return +{
+        map  { $_->init_arg => $_->get_value($instance) }
+        grep { $_->has_value($instance) }
+        grep { defined( $_->init_arg ) } $class->get_all_attributes
+    };
+#14...........
+        },
+
+        'git77.git77' => {
+            source => "git77",
+            params => "git77",
+            expect => <<'#15...........',
+# These should format about the same with -gal='Map Grep'.
+# NOTE: The braces only align if the internal code flag ALIGN_GREP_ALIASES is set
+    return +{
+        Map { $_->init_arg => $_->get_value($instance) }
+        Grep { $_->has_value($instance) }
+        Grep { defined( $_->init_arg ) } $class->get_all_attributes
+    };
+
+    return +{
+        map  { $_->init_arg => $_->get_value($instance) }
+        grep { $_->has_value($instance) }
+        grep { defined( $_->init_arg ) } $class->get_all_attributes
+    };
+#15...........
+        },
+
+        'vxl.def' => {
+            source => "vxl",
+            params => "def",
+            expect => <<'#16...........',
+# if equals is excluded then ternary is automatically excluded
+# side comment alignments always remain
+$co_description = ($color) ? 'bold cyan'  : '';           # description
+$co_prompt      = ($color) ? 'bold green' : '';           # prompt
+$co_unused      = ($color) ? 'on_green'   : 'reverse';    # unused
+#16...........
+        },
+
+        'vxl.vxl1' => {
+            source => "vxl",
+            params => "vxl1",
+            expect => <<'#17...........',
+# if equals is excluded then ternary is automatically excluded
+# side comment alignments always remain
+$co_description = ($color) ? 'bold cyan' : '';     # description
+$co_prompt = ($color) ? 'bold green' : '';         # prompt
+$co_unused = ($color) ? 'on_green' : 'reverse';    # unused
+#17...........
+        },
+
+        'vxl.vxl2' => {
+            source => "vxl",
+            params => "vxl2",
+            expect => <<'#18...........',
+# if equals is excluded then ternary is automatically excluded
+# side comment alignments always remain
+$co_description = ($color) ? 'bold cyan' : '';          # description
+$co_prompt      = ($color) ? 'bold green' : '';         # prompt
+$co_unused      = ($color) ? 'on_green' : 'reverse';    # unused
+#18...........
+        },
+
+        'bal.bal1' => {
+            source => "bal",
+            params => "bal1",
+            expect => <<'#19...........',
+{
+  L1:
+  L2:
+  L3:
+    return;
+};
+#19...........
+        },
+    };
+
+    my $ntests = 0 + keys %{$rtests};
+    plan tests => $ntests;
+}
+
+###############
+# EXECUTE TESTS
+###############
+
+foreach my $key ( sort keys %{$rtests} ) {
+    my $output;
+    my $sname  = $rtests->{$key}->{source};
+    my $expect = $rtests->{$key}->{expect};
+    my $pname  = $rtests->{$key}->{params};
+    my $source = $rsources->{$sname};
+    my $params = defined($pname) ? $rparams->{$pname} : "";
+    my $stderr_string;
+    my $errorfile_string;
+    my $err = Perl::Tidy::perltidy(
+        source      => \$source,
+        destination => \$output,
+        perltidyrc  => \$params,
+        argv        => '',             # for safety; hide any ARGV from perltidy
+        stderr      => \$stderr_string,
+        errorfile   => \$errorfile_string,    # not used when -se flag is set
+    );
+    if ( $err || $stderr_string || $errorfile_string ) {
+        print STDERR "Error output received for test '$key'\n";
+        if ($err) {
+            print STDERR "An error flag '$err' was returned\n";
+            ok( !$err );
+        }
+        if ($stderr_string) {
+            print STDERR "---------------------\n";
+            print STDERR "<<STDERR>>\n$stderr_string\n";
+            print STDERR "---------------------\n";
+            ok( !$stderr_string );
+        }
+        if ($errorfile_string) {
+            print STDERR "---------------------\n";
+            print STDERR "<<.ERR file>>\n$errorfile_string\n";
+            print STDERR "---------------------\n";
+            ok( !$errorfile_string );
+        }
+    }
+    else {
+        if ( !is( $output, $expect, $key ) ) {
+            my $leno = length($output);
+            my $lene = length($expect);
+            if ( $leno == $lene ) {
+                print STDERR
+"#> Test '$key' gave unexpected output.  Strings differ but both have length $leno\n";
+            }
+            else {
+                print STDERR
+"#> Test '$key' gave unexpected output.  String lengths differ: output=$leno, expected=$lene\n";
+            }
+        }
+    }
+}
diff --git a/t/snippets26.t b/t/snippets26.t
new file mode 100644 (file)
index 0000000..d46fcf1
--- /dev/null
@@ -0,0 +1,263 @@
+# Created with: ./make_t.pl
+
+# Contents:
+#1 bal.bal2
+#2 bal.def
+#3 lpxl.lpxl6
+
+# To locate test #13 you can search for its name or the string '#13'
+
+use strict;
+use Test::More;
+use Carp;
+use Perl::Tidy;
+my $rparams;
+my $rsources;
+my $rtests;
+
+BEGIN {
+
+    ###########################################
+    # BEGIN SECTION 1: Parameter combinations #
+    ###########################################
+    $rparams = {
+        'bal2'  => "-bal=2",
+        'def'   => "",
+        'lpxl6' => <<'----------',
+# equivalent to -lpxl='{ [ F(2'
+-lp -lpil='f(2'
+----------
+    };
+
+    ############################
+    # BEGIN SECTION 2: Sources #
+    ############################
+    $rsources = {
+
+        'bal' => <<'----------',
+{
+  L1:
+  L2:
+  L3: return;
+};
+----------
+
+        'lpxl' => <<'----------',
+# simple function call
+my $loanlength = getLoanLength(
+                                $borrower->{'categorycode'},    # sc1
+                                $iteminformation->{'itemtype'},
+                                $borrower->{'branchcode'}       # sc3
+);
+
+# function call, more than one level deep
+my $o = very::long::class::name->new(
+    {
+        propA => "a",
+        propB => "b",
+        propC => "c",
+    }
+);
+
+# function call with sublist
+debug(
+      "Connecting to DB.",
+      "Extra-Parameters: " . join("<->", $extra_parms),
+      "Config: " . join("<->", %config)
+     );
+
+# simple function call with code block
+$m->command(-label   => 'Save',
+            -command => sub { print "DOS\n"; save_dialog($win); });
+
+# function call, ternary in list
+return
+  OptArgs2::Result->usage(
+    $style == OptArgs2::STYLE_FULL ? 'FullUsage' : 'NormalUsage',
+    'usage: ' . $usage . "\n" );
+
+# not a function call
+%blastparam = (
+    -run            => \%runparam,
+    -file           => '',
+    -parse          => 1,
+    -signif         => 1e-5,
+);
+
+# 'local' is a keyword, not a user function
+    local (
+        $len,    $pts,      @colspec, $char, $cols,
+        $repeat, $celldata, $at_text, $after_text
+    );
+
+# square bracket with sublists
+$data = [
+         ListElem->new(id => 0, val => 100),
+         ListElem->new(id => 2, val => 50),
+         ListElem->new(id => 1, val => 10),
+        ];
+
+# curly brace with sublists
+$behaviour = {
+              cat   => {nap    => "lap",   eat  => "meat"},
+              dog   => {prowl  => "growl", pool => "drool"},
+              mouse => {nibble => "kibble"},
+             };
+----------
+    };
+
+    ####################################
+    # BEGIN SECTION 3: Expected output #
+    ####################################
+    $rtests = {
+
+        'bal.bal2' => {
+            source => "bal",
+            params => "bal2",
+            expect => <<'#1...........',
+{
+  L1: L2: L3: return;
+};
+#1...........
+        },
+
+        'bal.def' => {
+            source => "bal",
+            params => "def",
+            expect => <<'#2...........',
+{
+  L1:
+  L2:
+  L3: return;
+};
+#2...........
+        },
+
+        'lpxl.lpxl6' => {
+            source => "lpxl",
+            params => "lpxl6",
+            expect => <<'#3...........',
+# simple function call
+my $loanlength = getLoanLength(
+                                $borrower->{'categorycode'},    # sc1
+                                $iteminformation->{'itemtype'},
+                                $borrower->{'branchcode'}       # sc3
+);
+
+# function call, more than one level deep
+my $o = very::long::class::name->new(
+    {
+        propA => "a",
+        propB => "b",
+        propC => "c",
+    }
+);
+
+# function call with sublist
+debug(
+    "Connecting to DB.",
+    "Extra-Parameters: " . join( "<->", $extra_parms ),
+    "Config: " . join( "<->", %config )
+);
+
+# simple function call with code block
+$m->command(
+    -label   => 'Save',
+    -command => sub { print "DOS\n"; save_dialog($win); }
+);
+
+# function call, ternary in list
+return OptArgs2::Result->usage(
+    $style == OptArgs2::STYLE_FULL ? 'FullUsage' : 'NormalUsage',
+    'usage: ' . $usage . "\n" );
+
+# not a function call
+%blastparam = (
+    -run    => \%runparam,
+    -file   => '',
+    -parse  => 1,
+    -signif => 1e-5,
+);
+
+# 'local' is a keyword, not a user function
+local (
+    $len,    $pts,      @colspec, $char, $cols,
+    $repeat, $celldata, $at_text, $after_text
+);
+
+# square bracket with sublists
+$data = [
+    ListElem->new( id => 0, val => 100 ),
+    ListElem->new( id => 2, val => 50 ),
+    ListElem->new( id => 1, val => 10 ),
+];
+
+# curly brace with sublists
+$behaviour = {
+    cat   => { nap    => "lap",   eat  => "meat" },
+    dog   => { prowl  => "growl", pool => "drool" },
+    mouse => { nibble => "kibble" },
+};
+#3...........
+        },
+    };
+
+    my $ntests = 0 + keys %{$rtests};
+    plan tests => $ntests;
+}
+
+###############
+# EXECUTE TESTS
+###############
+
+foreach my $key ( sort keys %{$rtests} ) {
+    my $output;
+    my $sname  = $rtests->{$key}->{source};
+    my $expect = $rtests->{$key}->{expect};
+    my $pname  = $rtests->{$key}->{params};
+    my $source = $rsources->{$sname};
+    my $params = defined($pname) ? $rparams->{$pname} : "";
+    my $stderr_string;
+    my $errorfile_string;
+    my $err = Perl::Tidy::perltidy(
+        source      => \$source,
+        destination => \$output,
+        perltidyrc  => \$params,
+        argv        => '',             # for safety; hide any ARGV from perltidy
+        stderr      => \$stderr_string,
+        errorfile   => \$errorfile_string,    # not used when -se flag is set
+    );
+    if ( $err || $stderr_string || $errorfile_string ) {
+        print STDERR "Error output received for test '$key'\n";
+        if ($err) {
+            print STDERR "An error flag '$err' was returned\n";
+            ok( !$err );
+        }
+        if ($stderr_string) {
+            print STDERR "---------------------\n";
+            print STDERR "<<STDERR>>\n$stderr_string\n";
+            print STDERR "---------------------\n";
+            ok( !$stderr_string );
+        }
+        if ($errorfile_string) {
+            print STDERR "---------------------\n";
+            print STDERR "<<.ERR file>>\n$errorfile_string\n";
+            print STDERR "---------------------\n";
+            ok( !$errorfile_string );
+        }
+    }
+    else {
+        if ( !is( $output, $expect, $key ) ) {
+            my $leno = length($output);
+            my $lene = length($expect);
+            if ( $leno == $lene ) {
+                print STDERR
+"#> Test '$key' gave unexpected output.  Strings differ but both have length $leno\n";
+            }
+            else {
+                print STDERR
+"#> Test '$key' gave unexpected output.  String lengths differ: output=$leno, expected=$lene\n";
+            }
+        }
+    }
+}
diff --git a/t/testwide-passthrough.pl.src b/t/testwide-passthrough.pl.src
new file mode 100644 (file)
index 0000000..626de45
--- /dev/null
@@ -0,0 +1,5 @@
+# nothing to tidy or run
+"Plain";
+"Zwölf große Boxkämpfer jagen Vik quer über den Sylter.";
+"Jeż wlókł gęś. Uf! Bądź choć przy nim, stań!";
+"Любя, съешь щипцы, — вздохнёт мэр, — кайф жгуч.";
diff --git a/t/testwide-passthrough.t.SKIP b/t/testwide-passthrough.t.SKIP
new file mode 100644 (file)
index 0000000..6a00600
--- /dev/null
@@ -0,0 +1,130 @@
+use strict;
+use warnings;
+use utf8;
+
+use FindBin qw($Bin);
+use File::Temp qw(tempfile);
+use Test::More;
+
+BEGIN { unshift @INC, "./" }
+use Perl::Tidy;
+
+# This tests the -eos (--encode-output-strings) which was added for issue
+# git #83 to fix an issue with tidyall.
+
+# NOTE: to prevent automatic conversion of line endings LF to CRLF under github
+# Actions with Windows, which would cause test failure, it is essential that
+# there be a file 't/.gitattributes' with the line:
+# * -text
+
+# The test file has no tidying needs but is UTF-8 encoded, so all passes
+# through perltidy should read/write identical contents (previously only
+# file test behaved correctly)
+
+plan( tests => 6 );
+
+test_all();
+
+sub test_all {
+    my $test_file = "$Bin/testwide-passthrough.pl.src";
+    test_file2file($test_file);
+    test_scalar2scalar($test_file);
+    test_scalararray2scalararray($test_file);
+}
+
+sub test_file2file {
+    my $test_file = shift;
+
+    my $tmp_file = File::Temp->new( TMPDIR => 1 );
+
+    my $source      = $test_file;
+    my $destination = $tmp_file->filename();
+
+    note("Testing file2file: '$source' => '$destination'\n");
+
+    my $tidyresult = Perl::Tidy::perltidy(
+        argv        => '-utf8',
+        source      => $source,
+        destination => $destination
+    );
+    ok( !$tidyresult, 'perltidy' );
+
+    my $source_str      = slurp_raw($source);
+    my $destination_str = slurp_raw($destination);
+
+    my $source_hex      = unpack( 'H*', $source_str );
+    my $destination_hex = unpack( 'H*', $destination_str );
+    note("Comparing contents:\n  $source_hex\n  $destination_hex\n");
+
+    ok( $source_hex eq $destination_hex, 'file content compare' );
+}
+
+sub test_scalar2scalar {
+    my $testfile = shift;
+
+    my $source = slurp_raw($testfile);
+    my $destination;
+
+    note("Testing scalar2scalar\n");
+
+    my $tidyresult = Perl::Tidy::perltidy(
+        argv        => '-utf8 -eos',
+        source      => \$source,
+        destination => \$destination
+    );
+    ok( !$tidyresult, 'perltidy' );
+
+    my $source_hex      = unpack( 'H*', $source );
+    my $destination_hex = unpack( 'H*', $destination );
+
+    note("Comparing contents:\n  $source_hex\n  $destination_hex\n");
+    ok( $source_hex eq $destination_hex, 'scalar content compare' );
+}
+
+sub test_scalararray2scalararray {
+    my $testfile = shift;
+
+    my $source      = [ lines_raw($testfile) ];
+    my $destination = [];
+
+    note("Testing scalararray2scalararray\n");
+
+    my $tidyresult = Perl::Tidy::perltidy(
+        argv        => '-utf8 -eos',
+        source      => $source,
+        destination => $destination
+    );
+    ok( !$tidyresult, 'perltidy' );
+
+    my $source_str      = join( "", @$source );
+    my $destination_str = join( "", @$destination );
+
+    my $source_hex      = unpack( 'H*', $source_str );
+    my $destination_hex = unpack( 'H*', $destination_str );
+
+    note("Comparing contents:\n  $source_hex\n  $destination_hex\n");
+    ok( $source_hex eq $destination_hex, 'scalararray content compare' );
+}
+
+sub slurp_raw {
+    my $filename = shift;
+
+    open( TMP, '<', $filename );
+    binmode( TMP, ':raw' );
+    local $/;
+    my $contents = <TMP>;
+    close(TMP);
+
+    return $contents;
+}
+
+sub lines_raw {
+    my $filename = shift;
+
+    open( TMP, '<', $filename );
+    binmode( TMP, ':raw' );
+    my @contents = <TMP>;
+    close(TMP);
+
+    return @contents;
+}
diff --git a/t/testwide-tidy.pl.src b/t/testwide-tidy.pl.src
new file mode 100644 (file)
index 0000000..30bc8c1
--- /dev/null
@@ -0,0 +1,8 @@
+# really simple
+@a=
+(
+"Plain",
+"Zwölf große Boxkämpfer jagen Vik quer über den Sylter.",
+"Jeż wlókł gęś. Uf! Bądź choć przy nim, stań!",
+"Любя, съешь щипцы, — вздохнёт мэр, — кайф жгуч.",
+);
diff --git a/t/testwide-tidy.pl.srctdy b/t/testwide-tidy.pl.srctdy
new file mode 100644 (file)
index 0000000..bd478cc
--- /dev/null
@@ -0,0 +1,7 @@
+# really simple
+@a = (
+    "Plain",
+    "Zwölf große Boxkämpfer jagen Vik quer über den Sylter.",
+    "Jeż wlókł gęś. Uf! Bądź choć przy nim, stań!",
+    "Любя, съешь щипцы, — вздохнёт мэр, — кайф жгуч.",
+);
diff --git a/t/testwide-tidy.t.SKIP b/t/testwide-tidy.t.SKIP
new file mode 100644 (file)
index 0000000..723d088
--- /dev/null
@@ -0,0 +1,131 @@
+use strict;
+use warnings;
+use utf8;
+
+use FindBin qw($Bin);
+use File::Temp qw(tempfile);
+use Test::More;
+
+BEGIN { unshift @INC, "./" }
+use Perl::Tidy;
+
+# This tests the -eos (--encode-output-strings) which was added for issue
+# git #83 to fix an issue with tidyall.
+
+# NOTE: to prevent automatic conversion of line endings LF to CRLF under github
+# Actions with Windows, which would cause test failure, it is essential that
+# there be a file 't/.gitattributes' with the line:
+# * -text
+
+# The test file is UTF-8 encoded
+
+plan( tests => 6 );
+
+test_all();
+
+sub test_all {
+    my $test_file = "$Bin/testwide-tidy.pl.src";
+    my $tidy_file = "$Bin/testwide-tidy.pl.srctdy";
+    my $tidy_str  = slurp_raw($tidy_file);
+    test_file2file( $test_file, $tidy_str );
+    test_scalar2scalar( $test_file, $tidy_str );
+    test_scalararray2scalararray( $test_file, $tidy_str );
+}
+
+sub test_file2file {
+    my $test_file = shift;
+    my $tidy_str  = shift;
+    my $tidy_hex  = unpack( 'H*', $tidy_str );
+
+    my $tmp_file = File::Temp->new( TMPDIR => 1 );
+
+    my $source      = $test_file;
+    my $destination = $tmp_file->filename();
+
+    note("Testing file2file: '$source' => '$destination'\n");
+
+    my $tidyresult = Perl::Tidy::perltidy(
+        argv        => '-utf8',
+        source      => $source,
+        destination => $destination
+    );
+    ok( !$tidyresult, 'perltidy' );
+
+    my $destination_str = slurp_raw($destination);
+    my $destination_hex = unpack( 'H*', $destination_str );
+
+    note("Comparing contents:\n  $tidy_hex\n  $destination_hex\n");
+    ok($tidy_hex eq $destination_hex, 'file content compare');
+
+}
+
+sub test_scalar2scalar {
+    my $test_file = shift;
+    my $tidy_str  = shift;
+    my $tidy_hex  = unpack( 'H*', $tidy_str );
+
+    my $source = slurp_raw($test_file);
+    my $destination;
+
+    note("Testing scalar2scalar\n");
+
+    my $tidyresult = Perl::Tidy::perltidy(
+        argv        => '-utf8 -eos',
+        source      => \$source,
+        destination => \$destination
+    );
+    ok( !$tidyresult, 'perltidy' );
+
+    my $destination_hex = unpack( 'H*', $destination );
+
+    note("Comparing contents:\n  $tidy_hex\n  $destination_hex\n");
+    ok($tidy_hex eq $destination_hex, 'scalar content compare');
+
+}
+
+sub test_scalararray2scalararray {
+    my $test_file = shift;
+    my $tidy_str  = shift;
+    my $tidy_hex  = unpack( 'H*', $tidy_str );
+
+    my $source      = [ lines_raw($test_file) ];
+    my $destination = [];
+
+    note("Testing scalararray2scalararray\n");
+
+    my $tidyresult = Perl::Tidy::perltidy(
+        argv        => '-utf8 -eos',
+        source      => $source,
+        destination => $destination
+    );
+    ok( !$tidyresult, 'perltidy' );
+
+    my $destination_str = join( '', @$destination );
+    my $destination_hex = unpack( 'H*', $destination_str );
+
+    note("Comparing contents:\n  $tidy_hex\n  $destination_hex\n");
+    ok($tidy_hex eq $destination_hex, 'scalararray content compare');
+}
+
+sub slurp_raw {
+    my $filename = shift;
+
+    open( TMP, '<', $filename );
+    binmode( TMP, ':raw' );
+    local $/;
+    my $contents = <TMP>;
+    close(TMP);
+
+    return $contents;
+}
+
+sub lines_raw {
+    my $filename = shift;
+
+    open( TMP, '<', $filename );
+    binmode( TMP, ':raw' );
+    my @contents = <TMP>;
+    close(TMP);
+
+    return @contents;
+}