### ----------------------------------------------------
### -- App::pack.raku
### -- Licenses: GPL-3.0-or-later
### -- Authors: Francis Grizzly Smit <grizzly@smit.id.au>
### -- File: rakulib/Pack.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 4
0, 0) ~ '';    }
                }
        } elsif $colour {
                given $field {
                    when 'key'     {
                        my Str:D $type = %row«type»;
                        if $type eq 'dir' {
                            return t.color(0, 0, 255) ~ '  => ';
                        } else {
                            return t.color(0, 0, 255) ~ ' --> ';
                        }
                    }
                    when 'path'    { return t.color(0, 0, 255) ~ ' # '; }
                    when 'comment' { return t.color(0, 0, 255) ~ '  ';  }
                    default        { return t.color(255, 0, 0) ~ '';    }
                }
        } else {
                given $field {
                    when 'key'     {
                        my Str:D $type = %row«type»;
                        if $type eq 'dir' {
                            return '  => ';
                        } else {
                            return ' --> ';
                        }
                    }
                    when 'path'    { return ' # '; }
                    when 'comment' { return '  ';  }
                    default        { return '';    }
                }
        }
    } #`««« sub between(Int:D $idx, Str:D $field, Bool:D $colour, Bool:D $syntax, Str:D @fields, %row --> Str:D) »»»
    sub row-formatting(Int:D $cnt, Bool:D $colour, Bool:D $syntax --> Str:D) {
        if $colour {
            if $syntax { 
                return t.bg-color(255, 0, 255) ~ t.bold ~ t.bright-blue if $cnt == -3; # three heading lines. #
                return t.bg-color(0, 0, 127) ~ t.bold ~ t.bright-blue if $cnt == -2;
                return t.bg-color(255, 0, 255) ~ t.bold ~ t.bright-blue if $cnt == -1;
                return (($cnt % 2 == 0) ?? t.bg-yellow !! t.bg-color(0,195,0)) ~ t.bold ~ t.bright-blue;
            } else {
                return t.bg-color(255, 0, 255) ~ t.bold ~ t.bright-blue if $cnt == -3;
                return t.bg-color(0, 0, 127) ~ t.bold ~ t.bright-blue if $cnt == -2;
                return t.bg-color(255, 0, 255) ~ t.bold ~ t.bright-blue if $cnt == -1;
                return (($cnt % 2 == 0) ?? t.bg-yellow !! t.bg-color(0,195,0)) ~ t.bold ~ t.bright-blue;
            }
        } else {
            return '';
        }
    } #`««« sub row-formatting(Int:D $cnt, Bool:D $colour, Bool:D $syntax --> Str:D) »»»
    return list-by($prefix, $colour, $syntax, $page-length, $pattern, $key-name, @fields, %defaults, %the-lot,
                                            :&include-row, :&head-value, :&head-between, :&field-value, :&between, :&row-formatting);
} #`««« sub list-by-all(Str:D $prefix, Bool:D $colour is copy, Bool:D $syntax, Int:D $page-length, Regex:D $pattern --> Bool:D) is export »»»

sub add-tildes(Str:D $path is copy --> Str:D) {
    $path .=trim;
    unless $path.starts-with('~') {
        $path  = $path.IO.absolute;
    }
    $path  = '~' if $path eq $home;
    $path ~~ s{^ $home '/' } = '~/';
    $path ~~ s{^ '/home/' }  = '~';
    $path ~~ s!\/$!!;
    return $path;
}

sub add-path(Str:D $key, Str:D $path is copy, Bool $force, Str $comment --> Bool) is export {
    unless valid-key($key) {
        $*ERR.say: "invalid key: $key";
        return False;
    }
    unless $path.IO ~~ :d {
        $path = $path.IO.dirname;
    }
    if %the-lot{$key}:exists {
        if $force {
            CATCH {
                when X::IO::Rename {
                    $*ERR.say: $_;
                    return False;
                }
                default: {
                    $*ERR.say: $_;
                    return False;
                }
            }
            my Str $line = sprintf "%-20s  =>   %-50s", $key, add-tildes($path);
            with $comment {
                $line ~= " # $comment";
            }
            my IO::Handle:D $input  = "$config/pack.p_ck".IO.open:     :r, :nl-in("\n")   :chomp;
            my IO::Handle:D $output = "$config/pack.p_ck.new".IO.open: :w, :nl-out("\n"), :chomp(True);
            my Str $ln;
            while $ln = $input.get {
                if $ln ~~ rx/^ \h* <$key> \h* '=>' \h* .* $/ {
                    $output.say: $line;
                } else {
                    $output.say: $ln
                }
            }
            $input.close;
            $output.close;
            if "$config/pack.p_ck.new".IO.move: "$config/pack.p_ck" {
                return True;
            } else {
                return False;
            }
        } else {
            $*ERR.say: "duplicate key use -s|--set|--force to override";
            return False;
        }
    }
    my Str $config-path = "$config/pack.p_ck";
    my Str $line = sprintf "%-20s  =>   %-50s", $key, add-tildes($path);
    with $comment {
        $line ~= " # $comment";
    }
    $line ~= "\n";
    $config-path.IO.spurt($line, :append);
    return True;
} # sub add-path(Str:D $key, Str:D $path, Bool $force, Str $comment --> Bool) is export #

sub delete-key(Str:D $key, Bool:D $trash --> Bool) is export {
    CATCH {
        when X::IO::Rename {
            $*ERR.say: $_;
            return False;
        }
        default: {
            $*ERR.say: $_;
            return False;
        }
    }
    unless valid-key($key) {
        $*ERR.say: "invalid key: $key";
        return False;
    }
    my IO::Handle:D $input  = "$config/pack.p_ck".IO.open:     :r, :nl-in("\n")   :chomp;
    my IO::Handle:D $output = "$config/pack.p_ck.new".IO.open: :w, :nl-out("\n"), :chomp(True);
    my Str $ln;
    $ln = $input.get;
    while !$input.eof {
        if $ln ~~ rx/^ $key \h* [ '-->' || '=>' ] \h* [ <-[ # ]>+ ] \h* [ '#' \h* .* ]? $/ {
            if $trash {
                $output.say: "#$ln";
            }
        } else {
            $output.say: $ln
        }
        $ln = $input.get;
    }
    if $ln {
        if $ln ~~ rx/^ $key \h* [ '-->' || '=>' ] \h* [ <-[ # ]>+ ] \h* [ '#' \h* .* ]? $/ {
            if $trash {
                $output.say: "#$ln";
            }
        } else {
            $output.say: $ln
        }
    }
    $input.close;
    $output.close;
    if "$config/pack.p_ck.new".IO.move: "$config/pack.p_ck" {
        return True;
    } else {
        return False;
    }
} # sub delete-key(Str:D $key, Bool:D $trash --> Bool) is export #

sub tidy-file( --> Bool) is export {
    CATCH {
        when X::IO::Rename {
            $*ERR.say: $_;
            return False;
        }
        default: {
            $*ERR.say: $_;
            return False;
        }
    }
    my IO::Handle:D $input  = "$config/pack.p_ck".IO.open:     :r, :nl-in("\n")   :chomp(False);
    my Str $ln             = $input.get;
    my @lines;
    my Int:D $keywidth     = 20;
    my Int:D $pathwidth    = 50;
    my Int:D $commentwidth = 20;
    repeat {
        if $ln ~~ rx/^ \s* $<key> = [ \w+ [ <-[\h]>+ \w* ]* ] \s* $<type> = [ ' =>' ] \s* $<path> = [ <-[ # ]>+ ] \s* [ '#' \s* $<comment> = [ <-[#]>* ] [ \s* '#' \s* ]? ]?  $/ {
            my Str:D $key      = ~$<key>;
            $key              .=trim;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = ~$<comment>;
            $comment          .=trim;
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'normal';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ '#' \s* $<key> = [ \w+ [ <-[\h]>+ \w* ]* ] \s* $<type> = [ ' =>' ] \s* $<path> = [ <-[ # ]>+ ] \s* [ '#' \s* $<comment> = [ <-[#]>* ] [ '#' \s* ]? ]?  $/ {
            my Str:D $key      = ~$<key>;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = ~$<comment>;
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'commented';
            @lines.push: %line;
        } elsif $ln ~~ rx:ignorecase/^ '#' $<key> = [ 'key' ] \s+ $<type> = [ 'sep' ] \s+ $<path> = [ 'target' ] \s+ [ '#' $<comment> = [ \s* 'comment' ] [ \s* '#' \s* ]? ]? $/ {
            my Str:D $key      = ~$<key>;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = 'comment';
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'header';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ $<pre> = [ \s* ] '#' $<body> = [ .* ] $/ {
            my %line = pre => ~$<pre>, body => ~$<body>;
            %line«kind» = 'arbitrary';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ \s* $/ {
            my %line;
            %line«kind» = 'empty';
            @lines.push: %line;
        }
        $ln             = $input.get;
    }  until $input.eof;
    if $ln {
        if $ln ~~ rx/^ \s* $<key> = [ \w+ [ <-[\h]>+ \w* ]* ] \s* $<type> = [ ' =>' ] \s* $<path> = [ <-[ # ]>+ ] \s* [ '#' \s* $<comment> = [ <-[#]>* ] [ \s* '#' \s* ]? ]?  $/ {
            my Str:D $key      = ~$<key>;
            $key              .=trim;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = ~$<comment>;
            $comment          .=trim;
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'normal';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ '#' \s* $<key> = [ \w+ [ <-[\h]>+ \w* ]* ] \s* $<type> = [ ' =>' ] \s* $<path> = [ <-[ # ]>+ ] \s* [ '#' \s* $<comment> = [ <-[#]>* ] [ '#' \s* ]? ]?  $/ {
            my Str:D $key      = ~$<key>;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = ~$<comment>;
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'commented';
            @lines.push: %line;
        } elsif $ln ~~ rx:ignorecase/^ '#' $<key> = [ 'key' ] \s+ $<type> = [ 'sep' ] \s+ $<path> = [ 'target' ] \s+ [ '#' $<comment> = [ \s* 'comment' ] [ \s* '#' \s* ]? ]? $/ {
            my Str:D $key      = ~$<key>;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = 'comment';
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'header';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ $<pre> = [ \s* ] '#' $<body> = [ .* ] $/ {
            my %line = pre => ~$<pre>, body => ~$<body>;
            %line«kind» = 'arbitrary';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ \s* $/ {
            my %line;
            %line«kind» = 'empty';
            @lines.push: %line;
        }
    } # if $ln #
    $input.close;
    my IO::Handle:D $output = "$config/pack.p_ck.new".IO.open: :w, :nl-out("\n"), :chomp(True);
    for @lines -> %ln {
        my Str:D $line = '';
        given %ln«kind» {
            when 'normal' {
                $line = sprintf "%-*s %-3s %-*s # %-*s # ", $keywidth, %ln«key», %ln«type»,
                                        $pathwidth, %ln«path», $commentwidth, %ln«comment»;
            }
            when 'commented' {
                $line = sprintf "#%-*s %-3s %-*s # %-*s # ", $keywidth, %ln«key», %ln«type»,
                                        $pathwidth, %ln«path», $commentwidth, %ln«comment»;
            }
            when 'header' {
                $line = sprintf "#%-*s %-3s %-*s # %-*s # ", $keywidth - 1, %ln«key», %ln«type»,
                                        $pathwidth, %ln«path», $commentwidth, %ln«comment»;
            }
            when 'arbitrary' {
                $line = sprintf "%s#%s", %ln«pre», %ln«body»;
            }
        }
        $output.say: $line;
    }
    $output.close;
    if "$config/pack.p_ck.new".IO.move: "$config/pack.p_ck" {
        return True;
    } else {
        return False;
    }
} # sub tidy-file( --> Bool) is export #

enum Kind is export ( neither => 0, normal => 1, commented => 2, both => 3, );

sub is-kind(Str:D $kind --> Bool:D) is export {
    if $kind  ~~ rx/^ [ 'neither' || 'normal' || 'commented' || 'both' ] $/ {
        return True;
    } else {
        return False;
    }
} # sub is-kind(Str:D $kind --> Bool:D) is export #

sub add-comment(Str:D $key_, Str:D $comment_, Int:D $kind --> Bool) is export {
    CATCH {
        when X::IO::Rename {
            $*ERR.say: $_;
            return False;
        }
        default: {
            $*ERR.say: $_;
            return False;
        }
    }
    my IO::Handle:D $input  = "$config/pack.p_ck".IO.open:     :r, :nl-in("\n")   :chomp(False);
    my Str $ln             = $input.get;
    my @lines;
    my Int:D $keywidth     = 20;
    my Int:D $pathwidth    = 50;
    my Int:D $commentwidth = 20;
    repeat {
        if $ln ~~ rx/^ \s* $<key> = [ \w+ [ <-[\h]>+ \w* ]* ] \s* $<type> = [ ' =>' ] \s* $<path> = [ <-[ # ]>+ ] \s* [ '#' \s* $<comment> = [ <-[#]>* ] [ \s* '#' \s* ]? ]?  $/ {
            my Str:D $key      = ~$<key>;
            $key              .=trim;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = (~$<comment> ?? ~$<comment> !! '');
            $comment          .=trim;
            #dd $key, $path, $comment;
            $comment           = $comment_ if $key eq $key_ && $kind +& Kind::normal;
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'normal';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ '#' \s* $<key> = [ \w+ [ <-[\h]>+ \w* ]* ] \s* $<type> = [ ' =>' ] \s* $<path> = [ <-[ # ]>+ ] \s* [ '#' \s* $<comment> = [ <-[#]>* ] [ '#' \s* ]? ]?  $/ {
            my Str:D $key      = ~$<key>;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = (~$<comment> ?? ~$<comment> !! '');
            $comment          .=trim;
            $comment           = $comment_ if $key eq $key_ && $kind +& Kind::commented;
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'commented';
            @lines.push: %line;
        } elsif $ln ~~ rx:ignorecase/^ '#' $<key> = [ 'key' ] \s+ $<type> = [ 'sep' ] \s+ $<path> = [ 'target' ] \s+ [ '#' $<comment> = [ \s* 'comment' ] [ \s* '#' \s* ]? ]? $/ {
            my Str:D $key      = ~$<key>;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = 'comment';
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'header';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ $<pre> = [ \s* ] '#' $<body> = [ .* ] $/ {
            my %line = pre => ~$<pre>, body => ~$<body>;
            %line«kind» = 'arbitrary';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ \s* $/ {
            my %line;
            %line«kind» = 'empty';
            @lines.push: %line;
        }
        $ln             = $input.get;
    }  until $input.eof;
    if $ln {
        if $ln ~~ rx/^ \s* $<key> = [ \w+ [ <-[\h]>+ \w* ]* ] \s* $<type> = [ ' =>' ] \s* $<path> = [ <-[ # ]>+ ] \s* [ '#' \s* $<comment> = [ <-[#]>* ] [ \s* '#' \s* ]? ]?  $/ {
            my Str:D $key      = ~$<key>;
            $key              .=trim;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = (~$<comment> ?? ~$<comment> !! '');
            $comment          .=trim;
            #dd $key, $path, $comment;
            $comment           = $comment_ if $key eq $key_ && $kind +& Kind::normal;
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'normal';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ '#' \s* $<key> = [ \w+ [ <-[\h]>+ \w* ]* ] \s* $<type> = [ ' =>' ] \s* $<path> = [ <-[ # ]>+ ] \s* [ '#' \s* $<comment> = [ <-[#]>* ] [ '#' \s* ]? ]?  $/ {
            my Str:D $key      = ~$<key>;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = (~$<comment> ?? ~$<comment> !! '');
            $comment          .=trim;
            $comment           = $comment_ if $key eq $key_ && $kind +& Kind::commented;
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'commented';
            @lines.push: %line;
        } elsif $ln ~~ rx:ignorecase/^ '#' $<key> = [ 'key' ] \s+ $<type> = [ 'sep' ] \s+ $<path> = [ 'target' ] \s+ [ '#' $<comment> = [ \s* 'comment' ] [ \s* '#' \s* ]? ]? $/ {
            my Str:D $key      = ~$<key>;
            my Str:D $path     = ~$<path>;
            $path             .=trim;
            if ~$<type> eq ' =>' {
                $path          = add-tildes($path);
            }
            my Str:D $comment  = 'comment';
            $keywidth          = max($keywidth,            $key.chars);
            $pathwidth         = max($pathwidth,          $path.chars);
            $commentwidth      = max($commentwidth,    $comment.chars);
            my %line           = key => $key, type => ~$<type>, path => $path, comment => $comment;
            %line«kind» = 'header';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ $<pre> = [ \s* ] '#' $<body> = [ .* ] $/ {
            my %line = pre => ~$<pre>, body => ~$<body>;
            %line«kind» = 'arbitrary';
            @lines.push: %line;
        } elsif $ln ~~ rx/^ \s* $/ {
            my %line;
            %line«kind» = 'empty';
            @lines.push: %line;
        }
    } # if $ln #
    $input.close;
    my IO::Handle:D $output = "$config/pack.p_ck.new".IO.open: :w, :nl-out("\n"), :chomp(True);
    #dd @lines;
    for @lines -> %ln {
        my Str:D $line = '';
        given %ln«kind» {
            when 'normal' {
                $line = sprintf "%-*s %-3s %-*s # %-*s # ", $keywidth, %ln«key», %ln«type»,
                                        $pathwidth, %ln«path», $commentwidth, %ln«comment»;
            }
            when 'commented' {
                $line = sprintf "#%-*s %-3s %-*s # %-*s # ", $keywidth, %ln«key», %ln«type»,
                                        $pathwidth, %ln«path», $commentwidth, %ln«comment»;
            }
            when 'header' {
                $line = sprintf "#%-*s %-3s %-*s # %-*s # ", $keywidth - 1, %ln«key», %ln«type»,
                                        $pathwidth, %ln«path», $commentwidth, %ln«comment»;
            }
            when 'arbitrary' {
                $line = sprintf "%s#%s", %ln«pre», %ln«body»;
            }
        }
        $output.say: $line;
    }
    $output.close;
    if "$config/pack.p_ck.new".IO.move: "$config/pack.p_ck" {
        return True;
    } else {
        return False;
    }
}

sub list-commented(Str:D $prefix, Bool:D $colour, Bool:D $syntax, Int:D $page-length, Regex:D $pattern --> Bool) is export {
    my @data;
    my IO::Handle:D $input  = "$config/pack.p_ck".IO.open:     :r, :nl-in("\n")   :chomp;
    my $actions = CommentedLineActions;
    my $ln = $input.get;
    while !$input.eof {
        my %row;
        #my $test = Line.parse($ln, :enc('UTF-8'), :$actions).made;
        #dd $test;
        my %val = CommentedLine.parse($ln, :enc('UTF-8'), :$actions).made;
        my Str $key         = %val«key»;
        my %v               = %val«value»;
        my Str:D $type      = %v«type»;
        %row                = key => $key, type => $type;
        if $type eq 'commeted-alias' || $type eq 'commeted-dir' {
            my Str $path;
            with %v«path» {
                $path      = %v«path»;
                %row«path»  =  $path;
            }
            with %v«comment» {
                my Str $comment = %v«comment»;
                %row«comment»   = $comment;
            }
            @data.push: %row;
        }
        $ln = $input.get;
    } # while !$input.eof #
    if $ln {
        my %row;
        #my $test = Line.parse($ln, :enc('UTF-8'), :$actions).made;
        #dd $test;
        my %val = CommentedLine.parse($ln, :enc('UTF-8'), :$actions).made;
        my Str $key         = %val«key»;
        my %v               = %val«value»;
        my Str:D $type      = %v«type»;
        %row                = key => $key, type => $type;
        if $type eq 'commeted-alias' || $type eq 'commeted-dir' {
            my Str $path;
            with %v«path» {
                $path      = %v«path»;
                %row«path»  =  $path;
            }
            with %v«comment» {
                my Str $comment = %v«comment»;
                %row«comment»   = $comment;
            }
            @data.push: %row;
        }
    } # $ln #
    $input.close();
    my Str:D @fields = 'key', 'path', 'comment';
    my   %defaults;
    sub include-row(Str:D $prefix, Regex:D $pattern, Int:D $idx, Str:D @fields, %row --> Bool:D) {
        for @fields -> $field {
            my Str:D $value = '';
            with %row{$field} { #`««« if %row{$field} does not exist then a Any will be retured,
                                  and if some cases, you may return undefined values so use
                                  some sort of guard this is one way to do that, you could
                                  use %row{$field}:exists or :!exists or // perhaps.
                                  TIMTOWTDI rules as always. »»»
                $value = ~%row{$field};
            }
            return True if $value.starts-with($prefix, :ignorecase) && $value ~~ $pattern;
        }
        return False;
    } # sub include-row(Str:D $prefix, Regex:D $pattern, Str:D $key, @fields, %row --> Bool:D) #
    sub head-value(Int:D $indx, Str:D $field, Bool:D $colour, Bool:D $syntax, Str:D @fields --> Str:D) {
        if $syntax {
            t.color(0, 255, 255) ~ (($field eq 'key') ?? "#$field" !! $field);
        } elsif $colour {
            t.color(0, 255, 255) ~ (($field eq 'key') ?? "#$field" !! $field);
        } else {
            return (($field eq 'key') ?? "#$field" !! $field);
        }
    } #`««« sub head-value(Int:D $indx, Str:D $field, Bool:D $colour, Bool:D $syntax, Str:D @fields --> Str:D) »»»
    sub head-between(Int:D $indx, Str:D $field, Bool:D $colour, Bool:D $syntax, Str:D @fields --> Str:D) {
        if $colour {
            if $syntax {
                given $field {
                    when 'key'     { return t.color(0, 255, 255) ~ ' sep '; }
                    when 'path'    { return t.color(0, 255, 255)   ~ ' # ';   }
                    when 'comment' { return t.color(0, 0, 255)   ~ '  ';    }
                    default { return ''; }
                }
            } else {
                given $field {
                    when 'key'     { return t.color(0, 255, 255)   ~ ' sep '; }
                    when 'path'    { return t.color(0, 255, 255)   ~ ' # ';   }
                    when 'comment' { return t.color(0, 255, 255)   ~ '  ';    }
                    default { return ''; }
                }
            }
        } else {
            given $field {
                when 'key'     { return ' sep '; }
                when 'path'    { return ' # ';   }
                when 'comment' { return '  ';    }
                default        { return '';      }
            }
        }
    } #`««« sub head-between(Int:D $idx, Str:D $field, Bool:D $colour, Bool:D $syntax, Str:D @fields --> Str:D) »»»
    sub field-value(Int:D $idx, Str:D $field, $value, Bool:D $colour, Bool:D $syntax, Str:D @fields, %row --> Str:D) {
        if $syntax {
            given $field {
                when 'key'     { return t.color(0, 0, 255) ~ '#' ~ t.color(0, 255, 255) ~ ~$value; }
                when 'path'    { 
                    my Str:D $type = %row«type»;
                    if $type eq 'commeted-dir' {
                        return t.color(255, 0, 255) ~ ~$value;
                   