### ----------------------------------------------------
### -- Gzz::Text::Utils
### -- Licenses: LGPL-3.0-or-later
### -- Authors: Francis Grizzly Smit <grizzly@smit.id.au>
### -- File: rakulib/Gzz/Text/Utils.rakumod
### ----------------------------------------------------
### -- Chunk 6 of 8
-char;
                                              $result ~= right(sprintf($fmt, $arg), $width,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                          }
                                      }
                                  } else { # $padding eq something-else #
                                      if $width >= 0 { 
                                          if $precision >= 0 {
                                              $fmt ~= '.*';
                                              $fmt ~= $spec-char;
                                              if $justify eq '^' {
                                                  $result ~= centre(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              } elsif $justify eq '-' {
                                                  $result ~= left(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              } else {
                                                  $result ~= right(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              }
                                          } else {
                                              $fmt ~= $spec-char;
                                              if $justify eq '^' {
                                                  $result ~= centre(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              } elsif $justify eq '-' {
                                                  $result ~= left(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              } else {
                                                  $result ~= right(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              }
                                          }
                                      } else { # $width < 0 #
                                          if $precision >= 0 {
                                              $fmt ~= '.*';
                                              $fmt ~= $spec-char;
                                              if $justify eq '^' {
                                                  $result ~= centre(sprintf($fmt, $precision, $arg), $max-width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              } elsif $justify eq '-' {
                                                  $result ~= left(sprintf($fmt, $precision, $arg), $max-width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              } else {
                                                  $result ~= right(sprintf($fmt, $precision, $arg), $max-width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              }
                                          } else {
                                              $fmt ~= $spec-char;
                                              if $justify eq '^' {
                                                  $result ~= centre(sprintf($fmt, $arg), $max-width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              } elsif $justify eq '-' {
                                                  $result ~= left(sprintf($fmt, $arg), $max-width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              } else {
                                                  $result ~= right(sprintf($fmt, $arg), $max-width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                              }
                                          }
                                      }
                                  }
                             } # when 'g', 'G' #
                when 'b'|'B' {
                                 $arg .=Int;
                                 $max-width = max($max-width, $precision, 1) if $max-width > 0; #`« should not really have a both for this
                                                                                                    so munge together.
                                                                                                    Traditionally sprintf etc treat precision
                                                                                                    as max-width for Ints. »
                                 my Str:D $fmt = '%';
                                 $fmt ~= '+' if $force-sign;
                                 $fmt ~= 'v' if $vector;
                                 if $padding eq '0' {
                                     $fmt ~= '-' if $justify eq '-';
                                     $fmt ~= $padding;
                                     if $width >= 0 { # centre etc make no sense here #
                                         if $precision >= 0 {
                                             $fmt ~= '*.*';
                                             $fmt ~= $spec-char;
                                             if $type-prefix {
                                                 $result ~= right('0'~ $spec-char ~ sprintf($fmt, $width - 2, $precision - 2, $arg),
                                                                          $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $width, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } else {
                                             $fmt ~= '*';
                                             $fmt ~= $spec-char;
                                             if $type-prefix {
                                                 $result ~= right('0'~ $spec-char ~ sprintf($fmt, $width - 2, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $width, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         }
                                     } else {
                                         if $precision >= 0 {
                                             $fmt ~= '.*';
                                             $fmt ~= $spec-char;
                                             if $type-prefix {
                                                 $result ~= right('0'~ $spec-char ~ sprintf($fmt, $precision - 2, $arg), $precision, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $precision, $arg), $precision, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } else {
                                             $fmt ~= $spec-char;
                                             if $type-prefix {
                                                 $result ~= right('0'~ $spec-char ~ sprintf($fmt, $arg), $max-width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $arg), $max-width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         }
                                     }
                                 } elsif $padding eq ' ' {
                                     $fmt ~= '#' if $type-prefix;
                                     if $justify eq '^' {
                                         if $width >= 0 {
                                             if $precision >= 0 {
                                                 $fmt ~= '.*';
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } else { # $width < 0 #
                                             if $precision >= 0 {
                                                 $fmt ~= '.*';
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } # $width < 0 #
                                     } else { # justify is either '-' or '' i.e. left or right #
                                         if $precision >= 0 {
                                             $fmt ~= '.*';
                                             $fmt ~= $spec-char;
                                             if $justify eq '-' {
                                                 $result ~= left(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } else {
                                             $fmt ~= $spec-char;
                                             if $justify eq '-' {
                                                 $result ~= left(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         }
                                     } # justify is either '-' or '' i.e. left or right #
                                 } elsif $padding eq '' { # $padding eq '' #
                                     $fmt ~= '#' if $type-prefix;
                                     if $justify eq '^' {
                                         if $width >= 0 {
                                             if $precision >= 0 {
                                                 $fmt ~= '.*';
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $precision, $arg), $width,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $arg), $width,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } else { # $width < 0 #
                                             if $precision >= 0 {
                                                 $fmt ~= '.*';
                                                 $fmt ~= $spec-char;
                                                 $result ~= right(sprintf($fmt, $precision, $arg), $precision,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $fmt ~= $spec-char;
                                                 $result ~= right(sprintf($fmt, $arg), $max-width,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } # $width < 0 #
                                     } else { # justify is either '-' or '' i.e. left or right #
                                         if $precision >= 0 {
                                             $fmt ~= '.*';
                                             $fmt ~= $spec-char;
                                             if $justify eq '-' {
                                                 $result ~= left(sprintf($fmt, $precision, $arg), $width,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $precision, $arg), $width,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } else {
                                             $fmt ~= $spec-char;
                                             if $justify eq '-' {
                                                 $result ~= left(sprintf($fmt, $arg), $width,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $arg), $width,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         }
                                     } # justify is either '-' or '' i.e. left or right #
                                 } else { # $padding eq something-else #
                                     $fmt ~= '#' if $type-prefix;
                                     if $justify eq '^' {
                                         if $width >= 0 {
                                             if $precision >= 0 {
                                                 $fmt ~= '.*';
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } else { # $width < 0 #
                                             if $precision >= 0 {
                                                 $fmt ~= '.*';
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $fmt ~= $spec-char;
                                                 $result ~= centre(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } # $width < 0 #
                                     } else { # justify is either '-' or '' i.e. left or right #
                                         if $precision >= 0 {
                                             $fmt ~= '.*';
                                             $fmt ~= $spec-char;
                                             if $justify eq '-' {
                                                 $result ~= left(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $precision, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         } else {
                                             $fmt ~= $spec-char;
                                             if $justify eq '-' {
                                                 $result ~= left(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             } else {
                                                 $result ~= right(sprintf($fmt, $arg), $width, $padding,
                                                                               :number-of-chars(&internal-number-of-chars),
                                                                                                                   :$max-width, :$ellipsis);
                                             }
                                         }
                                     } # justify is either '-' or '' i.e. left or right #
                                 }
                             } # when 'b' #
            } # given $spec-char #
        } else {
            BadArg.new(:msg("Error: $?FILE line: $?LINE corrupted arg {@args[$cnt].WHAT.^name}")).throw;
        }
    } # for @format-str -> $arg #
    return $result;
    KEEP {
        &number-of-chars($total-number-of-chars, $total-number-of-visible-chars);
    }
} #`««« sub Sprintf(Str:D $format-str,
                :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                                        Str:D :$ellipsis = '', *@args --> Str) is export »»»

=begin pod

L<Top of Document|#table-of-contents>

=head2 Printf

=begin item

Same as B<C<Sprintf>> but writes it's output to B<C<$*OUT>> or an arbitrary filehandle if you choose.

=end item                                                

=begin item2

defined as

=begin code :lang<raku>

multi sub Printf(Str:D $format-str,
        :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                      Str:D :$ellipsis = '', *@args --> True) is export {
    Sprintf($format-str, :&number-of-chars, :$ellipsis, |@args).print;
} #`««« sub Printf(Str:D $format-str,
         :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                      Str:D :$ellipsis = '', *@args --> True) is export »»»

multi sub Printf(IO::Handle:D $fp, Str:D $format-str,
         :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                      Str:D :$ellipsis = '', *@args --> True) is export {
    $fp.print: Sprintf($format-str, :&number-of-chars, :$ellipsis, |@args);
} #`««« sub Printf(my IO::Handle:D $fp, Str:D $format-str,
         :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                      Str:D :$ellipsis = '', *@args --> True) is export »»»

=end code

=end item2

L<Top of Document|#table-of-contents>

=end pod

multi sub Printf(Str:D $format-str,
                :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                                        Str:D :$ellipsis = '', *@args --> True) is export {
    Sprintf($format-str, :&number-of-chars, :$ellipsis, |@args).print;
} #`««« sub Printf(Str:D $format-str,
                :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                                        Str:D :$ellipsis = '', *@args --> True) is export »»»

multi sub Printf(IO::Handle:D $fp, Str:D $format-str,
                :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                                        Str:D :$ellipsis = '', *@args --> True) is export {
    $fp.print: Sprintf($format-str, :&number-of-chars, :$ellipsis, |@args);
} #`««« sub Printf(my IO::Handle:D $fp, Str:D $format-str,
                :&number-of-chars:(Int:D, Int:D --> Bool:D) = &Sprintf-global-number-of-chars,
                                                        Str:D :$ellipsis = '', *@args --> True) is export »»»

=begin pod

=head3 MultiT

A lot of types but not Any.

=begin code :lang<raku>

subset MultiT is export of Any where * ~~  Str | Int | Rat | Num | Bool | Array;

=end code

=end pod

subset MultiT is export of Any where * ~~  Str | Int | Rat | Num | Bool | Array;

=begin pod

=head3 menu

Display a text based menu.

=begin code :lang<raku>

sub menu(@candidates is copy, Str:D $message = "",
                              :&row:(Int:D $c, Int:D $p, @a,
                                     Bool:D :$colour = False, Bool:D :$syntax = False,
                                     Str:D :$highlight-bg-colour = '',
                                     Str:D :$highlight-fg-colour = '',
                                     Str:D :$bg-colour0 = '',
                                     Str:D :$fg-colour0 = '', 
                                     Str:D :$bg-colour1 = '',
                                     Str:D :$fg-colour1 = '' --> Str:D) = &default-row, 
                              :&value:(Int:D $c, @a --> MultiT) = &default-value, 
                              Bool:D :c(:color(:$colour)) is copy = False,
                              Bool:D :s(:$syntax) = False, 
                              Str:D :$highlight-bg-colour = t.bg-color(0, 0, 127) ~ t.bold, 
                              Str:D :$highlight-fg-colour = t.bright-yellow, 
                              Str:D :$bg-colour0 = t.bg-yellow ~ t.bold, 
                              Str:D :$fg-colour0 = t.bright-blue, 
                              Str:D :$bg-colour1 = t.bg-color(0, 127, 0) ~ t.bold, 
                              Str:D :$fg-colour1 = t.bright-blue,  
                              Str:D :$bg-prompt = t.bg-green ~ t.bold, 
                              Str:D :$fg-prompt = t.bright-blue, 
                              Bool:D :$wrap-around = False --> MultiT) is export 

=end code

=item1 Where:
=item2 B<C<@candidates>> is an array of hashes to make up the rows of the menu.
=item2 B<C<$message>> is a message to be displayed at the top of the ascii text form of things (i.e. no colourising).
=item2 B<C<&row>> is is a callback to deal with the rows of the menu.
=item3 Where
=item4 B<C<$c>> is the current row count.
=item4 B<C<$p>> is the current position in the @candidates array.
=item4 B<C<@a>> is the array @candidates itself.
=item4 B<C<$highlight-bg-colour>> is the background colour of the current row (i.e. $c == $p).
=item4 B<C<$highlight-fg-colour>> is the foreground colour of the current row (i.e. $c == $p).
=item4 B<C<$bg-colour0>> is the background colour of the row (i.e. $c %% 2).
=item4 B<C<$fg-colour0>> is the foreground colour of the row (i.e. $c %% 2).
=item4 B<C<$bg-colour1>> is the background colour of the row (i.e. $c % 2 != 0 or not $c %% 2).
=item4 B<C<$fg-colour1>> is the foreground colour of the row (i.e. $c % 2 != 0).
=item2 B<C<&value>> is a callback to get the return value for the function.
=item4 B<C<$c>> is the row selected.
=item4 B<C<$c>> is the array @candidates.
=item2 B<C<:c(:color(:$colour))>> defines a boolean flag to tell whether to use colours or not.
=item3 you can use B<C<:c>>, B<C<:color>> or B<C<:colour>> for this they are all exactly the same.
=item2 B<C<:s(:$syntax)>> same as B<C<$colour>> except it could result in some sort of syntax highlighting. 
=item2 B<C<$highlight-bg-colour>>  the background colour to use to highlight the current line.
=item2 B<C<$highlight-fg-colour>>  the foreground colour to use to highlight the current line.
=item2 B<C<$bg-colour0>> the background colour to use if the line count is divisible by 2.
=item2 B<C<$fg-colour0>> the foreground colour to use if the line count is divisible by 2.
=item2 B<C<$bg-colour1>> the background colour to use if the line count is not divisible by 2.
=item2 B<C<$fg-colour1>> the foreground colour to use if the line count is not divisible by 2.
=item2 B<C<$bg-prompt>>  the background colour to use on the prompt line below the selection area.
=item2 B<C<$fg-prompt>>  the foreground colour to use on the prompt line below the selection area.
=item2 B<C<$wrap-around>> if true then the selection area wraps around, (i.e going past the end wraps around, instead of refusing to go there).
=item3 B<C<$highlight-bg-colour>> to B<C<$wrap-around>> are all just used for the dropdown case (i.e. B<C<$colour>> or B<C<$syntax>> are True)
=item3 B<C<$syntax>> is no different from B<C<$colour>> unless the user defines it using the B<C<:&row>> parameter.
=item4 calls L<dropdown|#dropdown> to do the colour work.

L<Top of Document|#table-of-contents>

=end pod

sub default-row(Int:D $cnt, Int:D $pos, @array,
                                     Bool:D :$colour = False, Bool:D :$syntax = False,
                                     Str:D :$highlight-bg-colour = '',
                                     Str:D :$highlight-fg-colour = '',
                                     Str:D :$bg-colour0 = '',
                                     Str:D :$fg-colour0 = '', 
                                     Str:D :$bg-colour1 = '',
                                     Str:D :$fg-colour1 = ''  --> Str:D) is export {
    if $colour || $syntax {
        if $cnt == $pos {
            return $highlight-bg-colour ~ $highlight-fg-colour ~ @array[$pos]«name»;
        } elsif $cnt %% 2 {
            return $bg-colour0 ~ $fg-colour0 ~ @array[$pos]«name»;
        } else {
            return $bg-colour1 ~ $fg-colour1 ~ @array[$pos]«name»;
        }
    } else {
        return @array[$pos]«name»;
    }
} #`««« sub default-row(Int:D $cnt, Int:D $pos, @array,
                                     Bool:D :$colour = False, Bool:D :$syntax = False,
                                     Str:D :$highlight-bg-colour = '',
                                     Str:D :$highlight-fg-colour = '',
                                     Str:D :$bg-colour0 = '',
                                     Str:D :$fg-colour0 = '', 
                                     Str:D :$bg-colour1 = '',
                                     Str:D :$fg-colour1 = ''  --> Str:D) is export »»»

sub default-value(Int:D $choice, @array --> MultiT) is export {
    return @array[$choice]«value»;
} # sub default-value(Int:D $choice, @array --> MultiT) is export #

sub menu(@candidates is copy, Str:D $message = "",
                              :&row:(Int:D $c, Int:D $p, @a,
                                     Bool:D :$colour = False, Bool:D :$syntax = False,
                                     Str:D :$highlight-bg-colour = '', Str:D :$highlight-fg-colour = '',
                                     Str:D :$bg-colour0 = '', Str:D :$fg-colour0 = '', 
                                     Str:D :$bg-colour1 = '', Str:D :$fg-colour1 = '' --> Str:D) = &default-row, 
                              :&value:(Int:D $c, @a --> MultiT) = &default-value, 
                              Bool:D :c(:color(:$colour)) is copy = False,
                              Bool:D :s(:$syntax) = False, 
                              Str:D :$highlight-bg-colour = t.bg-color(0, 0, 127) ~ t.bold, 
                              Str:D :$highlight-fg-colour = t.bright-yellow, 
                              Str:D :$bg-colour0 = t.bg-yellow ~ t.bold, 
                              Str:D :$fg-colour0 = t.bright-blue, 
                              Str:D :$bg-colour1 = t.bg-color(0, 127, 0) ~ t.bold, 
                              Str:D :$fg-colour1 = t.bright-blue,  
                              Str:D :$bg-prompt = t.bg-green ~ t.bold, 
                              Str:D :$fg-prompt = t.bright-blue, 
                              Bool:D :$wrap-around = False --> MultiT) is export {
    $colour = True if $syntax;
    my %cancel = value => 'cancel', name => 'cancel';
    @candidates.push(%cancel);
    if $colour {
        # insure that the screen is reset on error #
        my &stack = sub ( --> Nil) {
            while @signal {
                my &elt = @signal.pop;
                &elt();
            }
        };
        signal(SIGINT, SIGHUP, SIGQUIT, SIGTERM, SIGQUIT).tap( { &stack(); put t.restore-screen; say "$_ Caught"; exit 0 } );
        my &setup-option-str = sub (Int:D $cnt, Int:D $pos, @array --> Str:D ) {
            return &row($cnt, $pos, @array, :$colour, :$syntax, 
                        :$highlight-bg-colour, :$highlight-fg-colour,
                        :$bg-colour0, :$fg-colour0, 
                        :$bg-colour1, :$bg-colour1);
        };
        my &get-result = sub (MultiT:D $result, Int:D $pos, Int:D $length, @array --> MultiT:D ) {
            my $res = $result;
            if $pos ~~ 0..^$length {
                $res = @array[$pos]«value»;
            }
            return $res
        };
        my &find-pos = sub (MultiT $result, Int:D $pos is copy, @array --> Int:D) {
            for @array.kv -> $idx, %r {
                if %r«value» eq $result {
                    $pos = $idx;
                    last; # found so don't waste resources #
                }
            }
            return $pos;
        }
        my Str:D $result = dropdown(@candidates[@candidates.elems - 1]«value», 40, 'backup',
                                                    &setup-option-str, &find-pos, &get-result,
                                                    @candidates, 
                                                    :$highlight-bg-colour,
                                                    :$highlight-fg-colour,
                                                    :$bg-colour0,
                                                    :$fg-colour0,
                                                    :$bg-colour1,
                                                    :$fg-colour1,
                                                    :$bg-prompt,
                                                    :$fg-prompt,
                                                    :$wrap-around);
        return $result;
    }
    $message.say if $message;
    for @candidates.kv -> $indx, %row {
        my Str:D $candidate = &row($indx, $indx, @candidates);
        "%10d\t%-20s\n".printf($indx, $candidate)
    }
    "use cancel, bye, bye bye, quit, q, or {+@candidates - 1} to quit".say;
    my $choice = -1;
    loop {
        $choice = prompt("choose a candiate 0..{+@candidates - 1} =:> ");
        $choice = +@candidates - 1 if $choice ~~ rx:i/ ^^ \s* [ 'cancel' || 'bye' [ \s* 'bye' ] ? || 'quit' || 'q' ] \s* $$ /;
        if $choice !~~ rx/ ^^ \s* \d* \s* $$ / {
            "$choice: is not a valid option".say;
            redo
        }
        unless 0 <= $choice < +@candidates {
            "$choice: is not a valid option".say;
            redo;
        }
        last;
    }
    my Str $Dir;
    $Dir = &value($choice, @candidates) unless &row($choice,  $choice, @candidates) eq 'cancel';
    #$Dir.say;
    return $Dir;
} #`««« sub menu(@candidates is copy, Str:D $message = "",
                              :&row:(Int:D $c, Int:D $p, @a,
                                     Bool:D :$colour = False, Bool:D :$syntax = False,
                                     Str:D :$highlight-bg-colour = '', Str:D :$highlight-fg-colour = '',
                                     Str:D :$bg-colour0 = '', Str:D :$fg-colour0 = '', 
                                     Str:D :$bg-colour1 = '', Str:D :$fg-colour1 = '' --> Str:D) = &default-row, 
                              :&value:(Int:D $c, @a --> MultiT) = &default-value, 
                              Bool:D :c(:color(:$colour)) is copy = False,
                              Bool:D :s(:$syntax) = False, 
                              Str:D :$highlight-bg-colour = t.bg-color(0, 0, 127) ~ t.bold, 
                              Str:D :$highlight-fg-colour = t.bright-yellow, 
                              Str:D :$bg-colour0 = t.bg-yellow ~ t.bold, 
                              Str:D :$fg-colour0 = t.bright-blue, 
                              Str:D :$bg-colour1 = t.bg-color(0, 127, 0) ~ t.bold, 
                              Str:D :$fg-colour1 = t.bright-blue,  
                              Str:D :$bg-prompt = t.bg-green ~ t.bold, 
                              Str:D :$fg-prompt = t.bright-blue, 
                              Bool:D :$wrap-around = False --> MultiT) is export »»»

sub gzzreadline_call(Str:D $prompt, Str:D $prefill, Gzz_readline:D $gzzreadline --> Str ) is export {
    my Str $result;
    try {
        my $original-flags := Term::termios.new(:fd($*IN.native-descriptor)).getattr;
        @signal.push: {
            $original-flags.setattr(:NOW);
        }; # insure that we call the reset command if it dies on a signal #
        $result = $gzzreadline.gzzreadline($prompt, $prefill);
        $original-flags.setattr(:NOW);
        @signal.pop if @signal; # done without signal so remove our handler #
        CATCH {
            default {
                $original-flags.setattr(:NOW);
                @signal.pop if @signal;
                .backtrace;
                .Str.say; .rethrow 
            }
        }
    } # try #
    return $result;
} # sub gzzreadline_call(Str:D $prompt, Str:D $prefill, Gzz_readline:D $gzzreadline --> Str ) #

sub default-row-input-menu(Int:D $cnt, Int:D $pos, @array,
                                     Bool:D :$colour = False, Bool:D :$syntax = False,
                                     Str:D :$highlight-bg-colour = '',
                                     Str:D :$highlight-fg-colour = '',
                                     Str:D :$bg-colour0 = '',
                                     Str:D :$fg-colour0 = '', 
                                     Str:D :$bg-colour1 = '',
                                     Str:D :$fg-colour1 = ''  --> Str:D) is export {
    my %row = @array[$pos];
    my Str:D $value = '';
    given %row«type».tclc {
        when 'Int'   {
            my Int:D $val = %row«value».Int;
            $value = ~$val;
        }
        when 'Num'   {
            my Num:D $val = %row«value».Num;
            $value = ~$val;
        }
        when 'Rat'   {
            my Rat:D $val = %row«value».Rat;
            $value = ~$val;
        }
        when 'Bool'  {
            my Str:D $val = %row«value» ?? 'True' !! 'False';
            $value = $val;
        }
        when 'Str'   {
            $value = %row«value».Str;
        }
        when 'Array' {
            my @val = |%row«value»;
            $value = @val.join: ', ';
        }
        when 'Read-only' {
            $value = %row«value».Str;
        }
    }
    if $colour || $syntax {
        if $cnt == $pos {
            return $highlight-bg-colour ~ $highlight-fg-colour ~ $value;
        } elsif $cnt %% 2 {
            return $bg-colour0 ~ $fg-colour0 ~ $value;
        } else {
            return $bg-colour1 ~ $fg-colour1 ~ $value;
        }
    } else {
        return $value;
    }
} #`««« sub default-row-input-menu(Int:D $cnt, Int:D $pos, @array,
                                     Bool:D :$colour = False, Bool:D :$syntax = False,
                                     Str:D :$highlight-bg-colour = '',
                                     Str:D :$highlight-fg-colour = '',
                                     Str:D :$bg-colour0 = '',
                                     Str:D :$fg-colour0 = '', 
                                     Str:D :$bg-colour1 = '',
                                     Str:D :$fg-colour1 = ''  --> Str:D) is export »»»

sub default-prompt(Int:D $choice, @candidates,
                                     Bool:D :$colour = False, Bool:D :$syntax = False,
                                     Str:D :$bg-prompt = '', Str:D :$fg-prompt = '' --> Str:D) {
    my %candidate = @candidates[$choice];
    if $syntax {
        return $bg-prompt ~ $fg-prompt ~ %candidate«name» ~ ' > ';
    } elsif $colour {
        return $bg-prompt ~ $fg-prompt ~ %candidate«name» ~ ' > ';
    } else {
        return %candidate«name» ~ ' > ';
    }
} #`««« sub default-prompt(Int:D $choice, @candidates --> Str:D) »»»

sub default-edit(Int:D $choice, @candidates is copy, Str:D $edit --> Bool:D) {
    my %candidate = @candidates[$choice];
    given %candidate«type».tclc {
        when 'Int'   {
            my Int:D $val = +$edit;
            @candidates[$choice]«value