user
Task

Write and test a function over an indented plain text outline which either:

1. Returns a copy of the outline in which the sub-lists at every level of indentation are sorted, or
2. reports that the indentation characters or widths are not consistent enough to make the outline structure clear.

Your code should detect and warn of at least two types of inconsistent indentation:

* inconsistent use of whitespace characters (e.g. mixed use of tabs and spaces)
* inconsistent indent widths. For example, an indentation with an odd number of spaces in an outline in which the unit indent appears to be 2 spaces, or 4 spaces.

Your code should be able to detect and handle both tab-indented, and space-indented (e.g. 4 space, 2 space etc) outlines, without being given any advance warning of the indent characters used, or the size of the indent units.

You should also be able to specify different types of sort, for example, as a minimum, both ascending and descending lexical sorts.

Your sort should not alter the type or size of the indentation units used in the input outline.

(For an application of Indent Respectful Sort, see the Sublime Text package of that name. The Python source text [1] is available for inspection on Github).

**Tests**

* Sort every level of the (4 space indented) outline below lexically, once ascending and once descending.

```
zeta
    beta
    gamma
        lambda
        kappa
        mu
    delta
alpha
    theta
    iota
    epsilon
```

* Do the same with a tab-indented equivalent of the same outline.

```
zeta
	gamma
		mu
		lambda
		kappa
	delta
	beta
alpha
	theta
	iota
	epsilon
```

The output sequence of an ascending lexical sort of each level should be:

```
alpha
    epsilon
    iota
    theta
zeta
    beta
    delta
    gamma
        kappa
        lambda
        mu
```

The output sequence of a descending lexical sort of each level should be:

```
zeta
    gamma
        mu
        lambda
        kappa
    delta
    beta
alpha
    theta
    iota
    epsilon
```

* Attempt to separately sort each of the following two outlines, reporting any inconsistencies detected in their indentations by your validation code.

```
alpha
    epsilon
	iota
    theta
zeta
    beta
    delta
    gamma
    	kappa
        lambda
        mu
```

```
zeta
    beta
   gamma
        lambda
         kappa
        mu
    delta
alpha
    theta
    iota
    epsilon
```

Related tasks

:   * Functional\_coverage\_tree
    * Display\_an\_outline\_as\_a\_nested\_table
assistant
Rather than a monolithic verify-and-sort-and-print routine, implement as a series of routines that can be chained together or used separately as desired.

* Routine to check indent characters and return the indent white-space if it is consistent.

* Routine to import a text "outline" into a native data structure

* Routine(s) to output the data structure in the desire sort order.

```
my @tests = q:to/END/.split( /\n\n+/ )».trim;
zeta
    beta
    gamma
        lambda
        kappa
        mu
    delta
alpha
    theta
    iota
    epsilon

zeta
	gamma
		mu
		lambda
		kappa
	delta
	beta
alpha
	theta
	iota
	epsilon

alpha
    epsilon
	iota
    theta
zeta
    beta
    delta
    gamma
    	kappa
        lambda
        mu

zeta
    beta
   gamma
        lambda
         kappa
        mu
    delta
alpha
    theta
    iota
    epsilon
END

for @tests -> $t {
    say "{'=' x 55}\nUnsorted:\n$t";
    my $indent = try detect-indent $t;
    next unless $indent;
    say "\nSorted ascending:";
    pretty-print import($t, :level($indent) ).List, :ws($indent);
    say "\nSorted descending:";
    pretty-print import($t, :level($indent) ).List, :ws($indent), :desc;
}

sub detect-indent ($text) {
    my $consistent = $text.lines.map(* ~~ / ^ (\h*) /).join.comb.Set;
    note "\nUnwilling to continue; Inconsistent indent characters." and return '' if +$consistent > 1;
    my @ws = $text.lines.map: (* ~~ / ^ (\h*) /)».Str;
    my $indent = @ws.grep( *.chars > 0 ).min.first;
    note "\nUnwilling to continue; Inconsistent indentation." and return '' unless all
      @ws.map: { next unless .[0]; (.[0].chars %% $indent.chars) }
    $indent
}

sub import (Str $trees, :$level) {
    my $forest = '[';
    my $last = -Inf;
    for $trees.lines -> $branch {
        $branch ~~ / ($($level))* /;
        my $this = +$0;
        $forest ~= do {
            given $this cmp $last {
                when More { (?$this ?? q[ => \[ ] !! "" )~ "'{$branch.trim}'" }
                when Same { ", '{$branch.trim}'" }
                when Less { "{']' x $last - $this}, '{$branch.trim}' " }
            }
        }
        $last = $this;
    }
    $forest ~= ']' x 1 + $last;
    use MONKEY-SEE-NO-EVAL;
    $forest.EVAL;
}

multi pretty-print (List $struct, :$level = 0, :$ws = '    ', :$desc = False) {
    if $desc {
        pretty-print($_, :level($level), :$ws, :$desc ) for $struct.flat.sort.reverse.List
    } else {
        pretty-print($_, :level($level), :$ws, :$desc ) for $struct.flat.sort.List
    }
}

multi pretty-print (Pair $struct, :$level = 0, :$ws = '    ', :$desc = False) {
    say $ws x $level, $struct.key;
    pretty-print( $struct.value.sort( ).List, :level($level + 1), :$ws, :$desc )
}

multi pretty-print (Str $struct, :$level = 0, :$ws = '    ', :$desc = False) {
    say $ws x $level , $struct;
}

```

Output:

```
=======================================================
Unsorted:
zeta
    beta
    gamma
        lambda
        kappa
        mu
    delta
alpha
    theta
    iota
    epsilon

Sorted ascending:
alpha
    epsilon
    iota
    theta
zeta
    beta
    delta
    gamma
        kappa
        lambda
        mu

Sorted descending:
zeta
    gamma
        mu
        lambda
        kappa
    delta
    beta
alpha
    theta
    iota
    epsilon
=======================================================
Unsorted:
zeta
	gamma
		mu
		lambda
		kappa
	delta
	beta
alpha
	theta
	iota
	epsilon

Sorted ascending:
alpha
	epsilon
	iota
	theta
zeta
	beta
	delta
	gamma
		kappa
		lambda
		mu

Sorted descending:
zeta
	gamma
		mu
		lambda
		kappa
	delta
	beta
alpha
	theta
	iota
	epsilon
=======================================================
Unsorted:
alpha
    epsilon
	iota
    theta
zeta
    beta
    delta
    gamma
    	kappa
        lambda
        mu

Unwilling to continue; Inconsistent indent characters.
=======================================================
Unsorted:
zeta
    beta
   gamma
        lambda
         kappa
        mu
    delta
alpha
    theta
    iota
    epsilon

Unwilling to continue; Inconsistent indentation.
```