README
Table of Contents
NAME
Gzz::Text::Utils
AUTHOR
Francis Grizzly Smit ([email protected])
VERSION
v0.1.23
TITLE
Gzz::Text::Utils
SUBTITLE
A Raku module to provide text formatting services to Raku programs.
COPYRIGHT
LGPL V3.0+ LICENSE
Introduction
A Raku module to provide text formatting services to Raku programs.
Including a sprintf front-end Sprintf that copes better with Ansi highlighted text and implements %U and does octal as 0o123 or 0O123 if you choose %O as I hate ambiguity like 0123 is it an int with leading zeros or an octal number. Also there is %N for a new line and %T for a tab helpful when you want to use single quotes to stop the $ specs needing back slashes.
And a printf alike Printf.
Also it does centring and there is a max-width field in the % spec i.e. %*.*.*E, and more.
Motivations
When you embed formatting information into your text such as bold, italics, etc ... and colours standard text formatting will not work e.g. printf, sprintf etc also those functions don't do centring.
Another important thing to note is that even these functions will fail if you include such formatting in the text field unless you supply a copy of the text with out the formatting characters in it in the :ref field i.e. left($formatted-text, $width, :ref($unformatted-text)) or text($formatted-text, $width, :$ref) if the reference text is in a variable called $ref or you can write it as left($formatted-text, $width, ref => $unformatted-text)
Update
Fixed the proto type of left etc is now
sub left(Str:D $text, Int:D $width is copy, Str:D $fill = ' ',
:&number-of-chars:(Int:D, Int:D --> Bool:D) = &left-global-number-of-chars,
Str:D :$ref = strip-ansi($text), Int:D
:$max-width = 0, Str:D :$ellipsis = '' --> Str) is exportWhere sub strip-ansi(Str:D $text --> Str:D) is export is my new function for striping out ANSI escape sequences so we don't need to supply :$ref unless it contains codes that sub strip-ansi(Str:D $text --> Str:D) is export cannot strip out, if so I would like to know so I can update it to cope with these new codes.
Exceptions
BadArg
class BadArg is Exception is exportBadArg is a exception type that Sprintf will throw in case of badly specified arguments.
ArgParityMissMatch
class ArgParityMissMatch is Exception is exportArgParityMissMatch is an exception class that Sprintf throws if the number of arguments does not match what the number the format string says there should be.
NB: if you use num$ argument specs these will not count as they grab from the args add hoc, * width and precision spec however do count as they consume argument.
FormatSpecError
class FormatSpecError is Exception is exportFormatSpecError is an exception class that Format (used by Sprintf) throws if there is an error in the Format specification (i.e. %n instead of %N as %n is already taken, the same with using %t instead of %T).
Or anything else wrong with the Format specifier.
NB: %N introduces a \n character and %T a tab (i.e. \t).
Format and FormatActions
Format & FormatActions are a grammar and Actions pair that parse out the % spec and normal text chunks of a format string.
For use by Sprintf a sprintf alternative that copes with ANSI highlighted text.
UnhighlightBase & UnhighlightBaseActions and Unhighlight & UnhighlightActions
UnhighlightBase & UnhighlightBaseActions are a grammar & role pair that does the work required to to parse apart ansi highlighted text into ANSI highlighted and plain text.
Unhighlight & UnhighlightActions are a grammar & class pair which provide a simple TOP for applying an application of UnhighlightBase & UnhighlightBaseActions for use by sub strip-ansi(Str:D $text -- Str:D) is export> to strip out the plain text from a ANSI formatted string
The Functions Provided
strip-ansi
sub strip-ansi(Str:D $text --> Str:D) is exportStrips out all the ANSI escapes, at the moment just those provided by the
Terminal::ANSIorTerminal::ANSI::OOmodules both available asTerminal::ANSIfrom zef etc I am not sure how exhaustive that is, but I will implement any more escapes as I become aware of them.hwcswidth
sub hwcswidth(Str:D $text --> Int:D) is exportSame as
wcswidthbut it copes with ANSI escape sequences unlikewcswidth.The secret sauce is that it is defined as:
sub hwcswidth(Str:D $text --> Int:D) is export { return wcswidth(strip-ansi($text)); } # sub hwcswidth(Str:D $text --> Int:D) is export #
Here are 4 functions provided to centre, left and right justify text even when it is ANSI formatted.
centre
⋄
Centring text in a field.
sub centre(Str:D $text, Int:D $width is copy, Str:D $fill = ' ', :&number-of-chars:(Int:D, Int:D --> Bool:D) = ¢re-global-number-of-chars, Str:D :$ref = strip-ansi($text), Int:D :$max-width = 0, Str:D :$ellipsis = '' --> Str) is export {Centres the text
$textin a field of width$widthpadding either side with$fillWhere:
$fillis the fill char by default$fillis set to a single white space.If it requires an odd number of padding then the right hand side will get one more char/codepoint.
&number-of-charstakes a function which takes 2Int:D's and returns aBool:D.By default this is equal to the closure
centre-global-number-of-charswhich looks like:our $centre-total-number-of-chars is export = 0; our $centre-total-number-of-visible-chars is export = 0; sub centre-global-number-of-chars( Int:D $number-of-chars, Int:D $number-of-visible-chars --> Bool:D) { $centre-total-number-of-chars = $number-of-chars; $centre-total-number-of-visible-chars = $number-of-visible-chars; return True; }Which is a closure around the variables:
$centre-total-number-of-charsand$centre-total-number-of-visible-chars, these are globalourvariables thatGzz::Text::Utilsexports. But you can just usemyvariables from with a scope, just as well. And make thesublocal to the same scope.i.e.
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 { ... ... ... my Int:D $total-number-of-chars = 0; my Int:D $total-number-of-visible-chars = 0; sub internal-number-of-chars(Int:D $number-of-chars, Int:D $number-of-visible-chars --> Bool:D) { $total-number-of-chars += $number-of-chars; $total-number-of-visible-chars += $number-of-visible-chars; return True; } # sub internal-number-of-chars(Int:D $number-of-chars, Int:D $number-of-visible-chars --> Bool:D) # ... ... ... for @format-str -> %elt { my Str:D $type = %elt«type»; if $type eq 'literal' { my Str:D $lit = %elt«val»; $total-number-of-chars += $lit.chars; $total-number-of-visible-chars += strip-ansi($lit).chars; $result ~= $lit; } elsif $type eq 'fmt-spec' { ... ... ... given $spec-char { when 'c' { $arg .=Str; $ref .=Str; BadArg.new(:msg("arg should be one codepoint: {$arg.codes} found")).throw if $arg.codes != 1; $max-width = max($max-width, $precision, 0) if $max-width > 0; #`« should not really have a both for this so munge together. Traditionally sprintf etc treat precision as max-width for strings. » if $padding eq '' { if $justify eq '' { $result ~= right($arg, $width, :$ref, :number-of-chars(&internal-number-of-chars), :$max-width); } elsif $justify eq '-' { $result ~= left($arg, $width, :$ref, :number-of-chars(&internal-number-of-chars), :$max-width); } elsif $justify eq '^' { $result ~= centre($arg, $width, :$ref, :number-of-chars(&internal-number-of-chars), :$max-width); } } else { if $justify eq '' { $result ~= right($arg, $width, $padding, :$ref, :number-of-chars(&internal-number-of-chars), :$max-width); } elsif $justify eq '-' { $result ~= left($arg, $width, $padding, :$ref, :number-of-chars(&internal-number-of-chars), :$max-width); } elsif $justify eq '^' { $result ~= centre($arg, $width, $padding, :$ref, :number-of-chars(&internal-number-of-chars), :$max-width); } } } when 's' { ... ... ... ... ... ... ... ... ... 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 »»»
The parameter
:$refis by default set to the value ofstrip-ansi($text)This is used to obtain the length of the of the text using
wcswidth(Str)from module "Terminal::WCWidth" which is used to obtain the width the text if printed on the current terminal:NB:
wcswidthwill return -1 if you pass it text with colours etc embedded in them."
Terminal::WCWidth" is witten by bluebear94 github:bluebear94 get it with zef or whatever
:$max-widthsets the maximum width of the field but if set to0(The default), will effectively be infinite (∞).:$ellipsisis used to elide the text if it's too big I recommend either''the default or'…'.
left
⋄
Left Justifying text.
sub left(Str:D $text, Int:D $width is copy, Str:D $fill = ' ', :&number-of-chars:(Int:D, Int:D --> Bool:D) = &left-global-number-of-chars, Str:D :$ref = strip-ansi($text), Int:D :$max-width = 0, Str:D :$ellipsis = '' --> Str) is exportleftis the same except that except that it puts all the padding on the right of the field.
right
⋄
Right justifying text.
sub right(Str:D $text, Int:D $width is copy, Str:D $fill = ' ', :&number-of-chars:(Int:D, Int:D --> Bool:D) = &right-global-number-of-chars, Str:D :$ref = strip-ansi($text), Int:D :$max-width = 0, Str:D :$ellipsis = '' --> Str) is exportrightis again the same except it puts all the padding on the left and the text to the right.crop-field
text
Cropping Text in a field.
sub crop-field(Str:D $text, Int:D $w is rw, Int:D $width is rw, Bool:D $cropped is rw, Int:D $max-width, Str:D :$ellipsis = '' --> Str:D) is export {crop-fieldused bycentre,leftandrightto crop their input if necessary. Copes with ANSI escape codes.Where
$textis the text to be cropped possibly, wit ANSI escapes embedded.$wis used to hold the width of$textis read-write so will return that value.$widthis the desired width. Will be used to return the updated width.$croppedis used to return the status of whether or not$textwas truncated.$max-widthis the maximum width we are allowing.$ellipsisis used to supply a eliding . Empty string by default.
Sprintf
Sprintf like sprintf only it can deal with ANSI highlighted text. And has lots of other options, including the ability to specify a
$max-widthusingwidth.precision.max-width, which can be.*,*<num>$,.*, or<num>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 exportWhere:
format-stris is a superset of thesprintfformat string, but it has extra features: like the flag[ <char> ]where can be almost anything except[,]control characters, white space other than the normal space, andmax-widthafter the precision.The format string looks like this:
token format { <chunks>+ } token chunks { [ <chunk> || '%' <format-spec> ] } token chunk { <-[%]>+ } token format-spec { [ <fmt-esc> || <fmt-spec> ] } token fmt-esc { [ '%' #`« a literal % » || 'N' #`« a nl i.e. \n char but does not require interpolation so no double quotes required » || 'T' #`« a tab i.e. \t char but does not require interpolation so no double quotes required » || 'n' #`« not implemented and will not be, throws an exception if matched » || 't' #`« not implemented and will not be, throws an exception if matched » ] } token fmt-spec { [ <dollar-directive> '$' ]? <flags>? <width>? [ '.' <precision> [ '.' <max-width> ]? ]? <modifier>? <spec-char> }Where
dollar-directiveis a integer >= 1flagsis any zero or more of:+put a plus in front of positive values.-left justify, right is the default^centre justify.#ensure the leading0for any octal, prefix non-zero hexadecimal with0xor0X, prefix non-zero binary with0bor0Bvvector flag (used only with d directive)' 'pad with spaces.0pad with zeros.[ <char> ]pad with character char where char matches:<-[ <cntrl> \s \[ \] ]> || ' 'i.e. anything except control characters, white space (apart from the basic white space (i.e. \x20 or the one with ord 32)), and[and finally].
widthis either an integer or a*or a*followed by an integer >= 1 and a '$'.precisionis a.followed by either an positive integer or a*or a*followed by an integer >= 1 and a '$'.max-widthis a.followed by either an positive integer or a*or a*followed by an integer >= 1 and a '$'.modifierThese are not implemented but is one of:hhinterpret integer as a typecharorunsigned char.hinterpret integer as a typeshortorunsigned short.jinterpret integer as a typeintmax_t, only with a C99 compiler (unportable).linterpret integer as a typelongorunsigned long.llinterpret integer as a typelong long,unsigned long long, orquad(typically 64-bit integers).qinterpret integer as a typelong long,unsigned long long, orquad(typically 64-bit integers).Linterpret integer as a typelong long,unsigned long long, orquad(typically 64-bit integers).tinterpret integer as a typeptrdiff_t.zinterpret integer as a typesize_t.
spec-charor the conversion character is one of:ca character with the given codepoint.sa string.da signed integer, in decimal.uan unsigned integer, in decimal.oan unsigned integer, in octal, with a0oprepended if the#flag is present.xan unsigned integer, in hexadecimal, with a0xprepended if the#flag is present.ea floating-point number, in scientific notation.fa floating-point number, in fixed decimal notation.ga floating-point number, in %e or %f notation.Xlikex, but using uppercase letters, with a0Xprepended if the#flag is present.Elikee, but using an uppercaseE.Glikeg, but with an uppercaseE(if applicable).ban unsigned integer, in binary, with a0bprepended if the#flag is present.Ban unsigned integer, in binary, with a0Bprepended if the#flag is present.ia synonym for%d.Da synonym for%ld.Oa synonym for%lo.Fa synonym for%f.
:&number-of-charsis an optional named argument which takes a function with a signature:(Int:D, Int:D --Bool:D)> if not specified it will have the value of&Sprintf-global-number-of-charswhich is defined as:our $Sprintf-total-number-of-chars is export = 0; our $Sprintf-total-number-of-visible-chars is export = 0; sub Sprintf-global-number-of-chars(Int:D $number-of-chars, Int:D $number-of-visible-chars --> Bool:D) { $Sprintf-total-number-of-chars = $number-of-chars; $Sprintf-total-number-of-visible-chars = $number-of-visible-chars; return True }This is exactly the same as the argument by the same name in
centre,leftandrightabove.i.e.
sub test( --> True) is export { ... ... ... my $test-number-of-chars = 0; my $test-number-of-visible-chars = 0; sub test-number-of-chars(Int:D $number-of-chars, Int:D $number-of-visible-chars --> Bool:D) { $test-number-of-chars = $number-of-chars; $test-number-of-visible-chars = $number-of-visible-chars; return True } put Sprintf('%30.14.14s, %30.14.13s%N%%%N%^*.*s%3$*4$.*3$.*6$d%N%2$^[&]*3$.*4$.*6$s%T%1$[*]^100.*4$.99s', ${ arg => $highlighted, ref => $text }, $text, 30, 14, $highlighted, 13, :number-of-chars(&test-number-of-chars), :ellipsis('…')); dd $test-number-of-chars, $test-number-of-visible-chars; put Sprintf('%30.14.14s, testing %30.14.13s%N%%%N%^*.*s%3$*4$.*3$.*6$d%N%2$^[&]*3$.*4$.*6$s%T%1$[*]^100.*4$.99s', $[ $highlighted, $text ], $text, 30, 14, $highlighted, 13, 13, :number-of-chars(&test-number-of-chars), :ellipsis('…')); dd $test-number-of-chars, $test-number-of-visible-chars; ... ... ... }Note: This is a closure we should always use a closure if we want to get the number of characters printed.
:$ellipsisthis is an optional argument of typeStr:Dwhich defaults to'', if set will be used to mark elided text, if the argument is truncated due to exceeding the value ofmax-width(notemax-widthdefaults to0which means infinity). The recommended value would be something like….*@argsis an arbitrary long list of values each argument can be either a scalar value to be printed or a Hash or an ArrayIf a Hash then it should contain two pairs with keys:
argandref; denoting the actual argument and a reference argument respectively, the ref argument should be the same asargbut with no ANSI formatting etc to mess up the counting. As this ruins formatting spacing. If not present will be set tostrip-ansi($arg), only bother with all this ifstrip-ansi($arg)isn't good enough.If a Array then it should contain two values. The first being
argand the other beingref; everything else is the same as above.argthe actual argument.@args[$i][]the actual argument. Where$iis the current index into the array of args.@args[$i][1]the reference argument, as in the:$refarg of the left, right and centre functions which it uses. It only makes sense if your talking strings possibly formatted if not present will be set tostrip-ansi($arg)if arg otherwise.If it's a scalar then it's the argument itself. And
$refisstrip-ansi($arg)if arg>> otherwise.refthe reference argument, as in the:$refarg of the left, right and centre functions which it uses. It only makes sense if your talking strings possibly formatted if not present will be set tostrip-ansi($arg)if arg otherwise.i.e.
put Sprintf('%30.14.14s, %30.14.13s%N%%%N%^*.*s%3$*4$.*3$.*6$d%N%2$^[&]*3$.*4$.*6$s%T%1$[*]^100.*4$.99s', ${ arg => $highlighted, ref => $text }, $text, 30, 14, $highlighted, 13, :number-of-chars(&test-number-of-chars), :ellipsis('…')); dd $test-number-of-chars, $test-number-of-visible-chars; put Sprintf('%30.14.14s, testing %30.14.13s%N%%%N%^*.*s%3$*4$.*3$.*6$d%N%2$^[&]*3$.*4$.*6$s%T%1$[*]^100.*4$.99s', $[ $highlighted, $text ], $text, 30, 14, $highlighted, 13, 13, :number-of-chars(&test-number-of-chars), :ellipsis('…')); dd $test-number-of-chars, $test-number-of-visible-chars;
Printf
Same as
Sprintfbut writes it's output to$*OUTor an arbitrary filehandle if you choose.defined as
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 »»»
MultiT
A lot of types but not Any.
subset MultiT is export of Any where * ~~ Str | Int | Rat | Num | Bool | Array;menu
Display a text based menu.
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 exportWhere:
@candidatesis an array of hashes to make up the rows of the menu.$messageis a message to be displayed at the top of the ascii text form of things (i.e. no colourising).&rowis is a callback to deal with the rows of the menu.Where
$cis the current row count.$pis the current position in the @candidates array.@ais the array @candidates itself.$highlight-bg-colouris the background colour of the current row (i.e. p).$highlight-fg-colouris the foreground colour of the current row (i.e. p).$bg-colour0is the background colour of the row (i.e. $c %% 2).$fg-colour0is the foreground colour of the row (i.e. $c %% 2).$bg-colour1is the background colour of the row (i.e. c %% 2).$fg-colour1is the foreground colour of the row (i.e. $c % 2 != 0).
&valueis a callback to get the return value for the function.$cis the row selected.$cis the array @candidates.
:c(:color(:$colour))defines a boolean flag to tell whether to use colours or not.you can use
:c,:coloror:colourfor this they are all exactly the same.
:s(:$syntax)same as$colourexcept it could result in some sort of syntax highlighting.$highlight-bg-colourthe background colour to use to highlight the current line.$highlight-fg-colourthe foreground colour to use to highlight the current line.$bg-colour0the background colour to use if the line count is divisible by 2.$fg-colour0the foreground colour to use if the line count is divisible by 2.$bg-colour1the background colour to use if the line count is not divisible by 2.$fg-colour1the foreground colour to use if the line count is not divisible by 2.$bg-promptthe background colour to use on the prompt line below the selection area.$fg-promptthe foreground colour to use on the prompt line below the selection area.$wrap-aroundif true then the selection area wraps around, (i.e going past the end wraps around, instead of refusing to go there).$highlight-bg-colourto$wrap-aroundare all just used for the dropdown case (i.e.$colouror$syntaxare True)$syntaxis no different from$colourunless the user defines it using the:&rowparameter.calls dropdown to do the colour work.
input-menu(…)
sub input-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-input-menu,
:&value:(Int:D $c, @a --> MultiT) = &default-value,
:&elt-prompt:(Int:D $c, @a,
Bool:D :$colour = False, Bool:D :$syntax = False,
Str:D :$bg-prompt = '', Str:D :$fg-prompt = '' --> Str:D) = &default-prompt,
:&edit:(Int:D $c, @a is copy, Str:D $e --> Bool:D) = &default-edit,
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 exportWhere:
@candidatesis an array of hashes to make up the rows of the menu.$messageis a message to be displayed at the top of the ascii text form of things (i.e. no colourising).&rowis is a callback to deal with the rows of the menu.Where
$cis the current row count.$pis the current position in the @candidates array.@ais the array @candidates itself.$highlight-bg-colouris the background colour of the current row (i.e. p).$highlight-fg-colouris the foreground colour of the current row (i.e. p).$bg-colour0is the background colour of the row (i.e. $c %% 2).$fg-colour0is the foreground colour of the row (i.e. $c %% 2).$bg-colour1is the background colour of the row (i.e. c %% 2).$fg-colour1is the foreground colour of the row (i.e. $c % 2 != 0).
&valueis a callback to get the return value for the function.$cis the row selected.$cis the array @candidates.
&elt-prompt:(Int:D $c, @a, Bool:D :$colour = False, Bool:D :$syntax = False, Str:D :$bg-prompt = '', Str:D :$fg-prompt = '' --> Str:D) = &default-promptThe callback called by the function to get the prompts to let the user edit the value of the row.
$cis the entry choosen by the user.@ais the candidates array.:c(:color(:$colour))defines a boolean flag to tell whether to use colours or not.you can use
:c,:coloror:colourfor this they are all exactly the same.
:s(:$syntax)same as$colourexcept it could result in some sort of syntax highlighting.$bg-promptthe background colour to use on the prompt line below the selection area.$fg-promptthe foreground colour to use on the prompt line below the selection area.
&edit:(Int:D $c, @a is copy, Str:D $e --> Bool:D) = &default-editThe callback called by the function to set the new value of the row.
$cis the entry choosen by the user.@ais the candidates array.
:c(:color(:$colour))defines a boolean flag to tell whether to use colours or not.you can use
:c,:coloror:colourfor this they are all exactly the same.
:s(:$syntax)same as$colourexcept it could result in some sort of syntax highlighting.$highlight-bg-colourthe background colour to use to highlight the current line.$highlight-fg-colourthe foreground colour to use to highlight the current line.$bg-colour0the background colour to use if the line count is divisible by 2.$fg-colour0the foreground colour to use if the line count is divisible by 2.$bg-colour1the background colour to use if the line count is not divisible by 2.$fg-colour1the foreground colour to use if the line count is not divisible by 2.$bg-promptthe background colour to use on the prompt line below the selection area.$fg-promptthe foreground colour to use on the prompt line below the selection area.$wrap-aroundif true then the selection area wraps around, (i.e going past the end wraps around, instead of refusing to go there).$highlight-bg-colourto$wrap-aroundare all just used for the dropdown case (i.e.$colouror$syntaxare True)$syntaxis no different from$colourunless the user defines it using the:&rowparameter.calls dropdown to do the colour work.
NB: the colours stuff is not yet implemented
dropdown(…)
A text based dropdown/list or menu with ANSI colours.
sub dropdown(MultiT:D $id, Int:D $window-height is copy, Str:D $id-name,
&setup-option-str:(Int:D $c, Int:D $p, @a --> Str:D),
&find-pos:(MultiT $r, Int:D $p, @a --> Int:D),
&get-result:(MultiT:D $res, Int:D $p, Int:D $l, @a --> MultiT:D),
@array,
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 exportWhere
$idis the starting value of our position in the array/choices.$window-heightis the number of rows of characters to display at a time.$id-nameis the name of the parameter we are scrolling.&setup-option-stris a function that returns the current row.Where:
the arg
$cwill be the count of the row we are drawing.the arg
$pwill be the position in the array we are at.the arg
@awill be the@arraysupplied todropdown(…)the use of a function for this means you can compute a much more complex field.
&find-posis a function that finds the start position in thedropdown.Where:
the arg
$ris the value in the array@arrayto look for.the arg
$pis the best approximation of where it might be if you are using it in a loop or something it could be where it last was.the arg
@athe argument@arraythat was passed todropdown.you can name these argument anything you like in you function, and because of the computed nature of this function and the other two you have great flexibility.
&get-resultis a function to work out the value selected.Where:
the arg
$resis the default value to return.the arg
$pis the current position in the array@arraysupplied todropdown.the arg
$lis the length of the array@array.the arg
@ais the array@arraythat was supplied todropdown.
@arraythe array of rows to display.$highlight-bg-colourthe background colour to use to highlight the current line.$highlight-fg-colourthe foreground colour to use to highlight the current line.$bg-colour0the background colour to use if the line count is divisible by 2.$fg-colour0the foreground colour to use if the line count is divisible by 2.$bg-colour1the background colour to use if the line count is not divisible by 2.$fg-colour1the foreground colour to use if the line count is not divisible by 2.$bg-promptthe background colour to use on the prompt line below the selection area.$fg-promptthe foreground colour to use on the prompt line below the selection area.$wrap-aroundif true then the selection area wraps around, (i.e going past the end wraps around, instead of refusing to go there).Because we use a function we can compute much more complex results; depending on what we have in
@array. The result can be any of Str, Int, Rat or Num see MultiT.
Here is an example of use.
my &setup-option-str = sub (Int:D $cnt, Int:D $p, @array --> Str:D ) {
my Str $name;
my Str $cc;
my Str $flag;
my Str $prefix;
if $cnt < 0 {
$name = "No country selected yet.";
$cc = "";
$flag = "";
$prefix = "you must choose one";
} else {
my %row = @array[$cnt];
$name = %row«_name»;
$cc = %row«cc»;
try {
CATCH {
default {
my $Name = $name;
$Name ~~ s:g/ <wb> 'and' <wb> /\&/;
try {
CATCH {
default { $flag = uniparse 'PENGUIN'}
}
$flag = uniparse $Name;
}
}
}
$flag = uniparse $name;
}
$prefix = %row«prefix»;
}
return "$flag $name: $cc ($prefix)"
};
my &find-pos = sub (MultiT $result, Int:D $pos, @array --> Int:D) {
for @array.kv -> $idx, %r {
if %r{$id-name} == $result {
$pos = $idx;
last; # found so don't waste resources #
}
}
return $pos;
}
my &get-result = sub (MultiT:D $result, Int:D $pos, Int:D $length, @array --> MultiT:D ) {
my $res = $result;
if $pos ~~ 0..^$length {
my %row = |%(@array[$pos]);
$res = %row«id» if %row«id»:exists;
}
return $res
};
my Int:D $cc-id = dropdown($cc_id, 20, 'id',
&setup-option-str, &find-pos, &get-result, @_country);
while !valid-country-cc-id($cc-id, %countries) {
$cc-id = dropdown($cc-id, 20, 'id',
&setup-option-str, &find-pos, &get-result, @_country);
}Or using a much simpler array. NB: from menu
my &setup-option-str = sub (Int:D $cnt, Int:D $pos, @array --> Str:D ) {
return @array[$cnt];
};
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];
}
return $res
};
my &find-pos = sub (MultiT $result, Int:D $pos, @array --> Int:D) {
for @array.kv -> $idx, $r {
if $r eq $result {
$pos = $idx;
last; # found so don't waste resources #
}
}
return $pos;
}
my Str:D $result = dropdown(@candidates[@candidates.elems - 1], 40, 'backup',
&setup-option-str, &find-pos, &get-result, @candidates);lead-dots(…)
Returns $text in a field of $width with a line of dots preceding it. Sort of like left with $fill defaulting to . but with a single space between the text and the padding.
sub lead-dots(Str:D $text, Int:D $width is copy, Str:D $fill = '.' --> Str) is exportWhere:
$textthe text to be preceded by the dots.$widththe width of the total field.$fillthe fill char or string.
trailing-dots(…)
Returns $text in a field of $width with a line of dots trailing after it. Sort of like right with $fill defaulting to . but with a single space between the text and the padding.
sub trailing-dots(Str:D $text, Int:D $width is copy, Str:D $fill = '.' --> Str) is exportWhere:
$textthe text to be trailed by the dots.$widththe width of the total field.$fillthe fill char or string.
dots(…)
Returns $text in a field of $width with a line of dots preceding it. Sort of like left with $fill defaulting to ..
sub dots(Str:D $text, Int:D $width is copy, Str:D $fill = '.' --> Str) is exportWhere:
$textthe text to be preceded by the dots.$widththe width of the total field.$fillthe fill char or string.