| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Text::PrettyTable; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Text::PrettyTable - Allow for auto-fixed-width formatting of raw data |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
This module doesn't require any dependencies. |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=cut |
|
12
|
|
|
|
|
|
|
|
|
13
|
6
|
|
|
6
|
|
795515
|
use strict; |
|
|
6
|
|
|
|
|
13
|
|
|
|
6
|
|
|
|
|
243
|
|
|
14
|
6
|
|
|
6
|
|
36
|
use warnings; |
|
|
6
|
|
|
|
|
42
|
|
|
|
6
|
|
|
|
|
448
|
|
|
15
|
6
|
|
|
6
|
|
46
|
use base qw(Exporter); |
|
|
6
|
|
|
|
|
10
|
|
|
|
6
|
|
|
|
|
13171
|
|
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
our $VERSION = '0.03'; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our @border = ('| ', ' | ', ' |', ' ', |
|
20
|
|
|
|
|
|
|
'+-', '-+-', '-+', '-', |
|
21
|
|
|
|
|
|
|
'+-', '-+-', '-+', '-', |
|
22
|
|
|
|
|
|
|
'+-', '-+-', '-+', '-'); |
|
23
|
|
|
|
|
|
|
our @borderu = ("│ ", " │ ", " │", ' ', |
|
24
|
|
|
|
|
|
|
'┌─', '─┬─', '─┐', '─', |
|
25
|
|
|
|
|
|
|
'├─', '─┼─', '─┤', '─', |
|
26
|
|
|
|
|
|
|
'└─', '─┴─', '─┘', '─'); |
|
27
|
|
|
|
|
|
|
our $unibox = 1; |
|
28
|
|
|
|
|
|
|
our $split = 100; |
|
29
|
|
|
|
|
|
|
our $qr_escape = "[^ -~]"; |
|
30
|
|
|
|
|
|
|
our @EXPORT = qw(pretty_table); |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub new { |
|
33
|
15
|
|
|
15
|
0
|
574732
|
my $class = shift; |
|
34
|
15
|
100
|
|
|
|
107
|
return bless ref($_[0]) ? $_[0] : {@_}, $class; |
|
35
|
|
|
|
|
|
|
} |
|
36
|
|
|
|
|
|
|
|
|
37
|
2
|
|
|
2
|
1
|
161632
|
sub pretty_table { __PACKAGE__->tablify(@_) } |
|
38
|
1
|
|
|
1
|
1
|
1517
|
sub plain_text { goto &tablify } |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub tablify { |
|
41
|
26
|
|
|
26
|
1
|
240363
|
my ($self, $data, $args) = @_; |
|
42
|
26
|
100
|
|
|
|
112
|
if (!ref $self) { |
|
|
|
100
|
|
|
|
|
|
|
43
|
4
|
|
100
|
|
|
26
|
$self = $self->new($args || {}); |
|
44
|
|
|
|
|
|
|
} elsif ($args) { |
|
45
|
|
|
|
|
|
|
# Override settings in new object |
|
46
|
7
|
|
|
|
|
36
|
my $new_p = __PACKAGE__->new({ %$self, %$args }); |
|
47
|
|
|
|
|
|
|
# Clean call without $args |
|
48
|
7
|
|
|
|
|
41
|
return $new_p->tablify($data); |
|
49
|
|
|
|
|
|
|
} |
|
50
|
19
|
|
50
|
|
|
125
|
local $self->{'_level'} = 1 + ($self->{'_level'} || 0); |
|
51
|
|
|
|
|
|
|
|
|
52
|
19
|
100
|
|
|
|
58
|
my $uni = exists($self->{'unibox'}) ? $self->{'unibox'} : $unibox; |
|
53
|
19
|
50
|
33
|
|
|
102
|
local $split = $self->{'split'} || $split if $self->{'_level'} == 1; |
|
54
|
240
|
|
|
|
|
561
|
local @border = ref($uni) ? @$uni : map {utf8::decode(my $c = $_); $c} @borderu |
|
|
240
|
|
|
|
|
553
|
|
|
55
|
19
|
50
|
33
|
|
|
105
|
and !$uni or local $qr_escape = "[^ -~".join('', @border)."]" if $uni && $self->{'_level'} == 1; |
|
|
|
100
|
33
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
56
|
|
|
|
|
|
|
|
|
57
|
19
|
|
|
|
|
72
|
my @bucket; |
|
58
|
|
|
|
|
|
|
my @title; |
|
59
|
19
|
|
|
|
|
0
|
my @max; |
|
60
|
19
|
|
|
|
|
0
|
my @dir; |
|
61
|
|
|
|
|
|
|
my $add = sub { |
|
62
|
32
|
|
|
32
|
|
66
|
my ($cols, $bucket) = @_; |
|
63
|
32
|
50
|
|
|
|
77
|
if (!ref($cols)) { |
|
64
|
0
|
0
|
|
|
|
0
|
for my $chunk ($split ? map {/(.{$split}|.+)/g} split /\n/, $cols : split /\n/, $cols) { |
|
|
0
|
|
|
|
|
0
|
|
|
65
|
0
|
|
|
|
|
0
|
push @$bucket, $chunk; |
|
66
|
|
|
|
|
|
|
} |
|
67
|
0
|
|
|
|
|
0
|
return; |
|
68
|
|
|
|
|
|
|
} |
|
69
|
32
|
|
|
|
|
61
|
my $i = @$bucket; |
|
70
|
32
|
|
|
|
|
112
|
for my $j (0 .. $#$cols) { |
|
71
|
78
|
|
|
|
|
154
|
my $_split = $split; |
|
72
|
78
|
|
|
|
|
163
|
my $val = $cols->[$j]; |
|
73
|
78
|
50
|
|
|
|
276
|
if (! defined $val) { |
|
|
|
100
|
|
|
|
|
|
|
74
|
0
|
|
|
|
|
0
|
$val = '(undef)'; |
|
75
|
|
|
|
|
|
|
} elsif (ref $val) { |
|
76
|
1
|
50
|
|
|
|
4
|
if (UNIVERSAL::isa($val, 'SCALAR')) { |
|
77
|
1
|
50
|
33
|
|
|
16
|
$val = (defined(&JSON::true) && JSON::true() eq $val) ? '(true)' |
|
|
|
50
|
33
|
|
|
|
|
|
78
|
|
|
|
|
|
|
: (defined(&JSON::false) && JSON::false() eq $val) ? '(false)' |
|
79
|
|
|
|
|
|
|
: "\\\"$$val\""; |
|
80
|
|
|
|
|
|
|
} else { |
|
81
|
0
|
|
|
|
|
0
|
chomp($val = $self->tablify($val)); |
|
82
|
0
|
0
|
0
|
|
|
0
|
$_split = 0 if $_split && $val =~ /^\Q$border[4]\E/ && $val =~ /\Q$border[14]\E$/; |
|
|
|
|
0
|
|
|
|
|
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
} |
|
85
|
78
|
100
|
100
|
|
|
442
|
$dir[$j] = 1 if $val =~ /\D/ && $bucket == \@bucket; # TODO - we could work on our alignment |
|
86
|
78
|
|
|
|
|
159
|
my $I = $i; |
|
87
|
78
|
50
|
|
|
|
261
|
for my $chunk ($_split ? map {/(.{$_split}|.+)/g} split /\n/, $val : split /\n/, $val) { |
|
|
79
|
|
|
|
|
688
|
|
|
88
|
79
|
|
|
|
|
683
|
$chunk =~ s/($qr_escape)/sprintf "\\%03o", ord $1/eg; |
|
|
6
|
|
|
|
|
20
|
|
|
89
|
79
|
|
|
|
|
222
|
$bucket->[$I++]->[$j] = $chunk; |
|
90
|
79
|
100
|
100
|
|
|
426
|
$max[$j] = length($chunk) if !$max[$j] || $max[$j] < length($chunk); |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
} |
|
93
|
19
|
|
|
|
|
174
|
}; |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
|
|
96
|
19
|
100
|
|
|
|
76
|
if (UNIVERSAL::isa($data, 'HASH')) { |
|
|
|
50
|
|
|
|
|
|
|
97
|
14
|
|
|
|
|
26
|
my $title = $self->{'title'}; |
|
98
|
14
|
50
|
|
|
|
51
|
$add->($title, \@title) if $title; |
|
99
|
14
|
0
|
|
|
|
21
|
my @keys = @{ $self->{'sort'} || [sort {($a eq 'id') ? -1 : ($b eq 'id') ? 1 : $a cmp $b } keys %$data] }; |
|
|
14
|
0
|
|
|
|
93
|
|
|
|
0
|
50
|
|
|
|
0
|
|
|
100
|
14
|
|
|
|
|
89
|
$add->([$_, $data->{$_}], \@bucket) for @keys; |
|
101
|
14
|
50
|
|
|
|
50
|
$add->(['(empty hash)'], \@bucket) if !@bucket; |
|
102
|
|
|
|
|
|
|
} elsif (UNIVERSAL::isa($data, 'ARRAY')) { |
|
103
|
5
|
|
|
|
|
11
|
my %title; |
|
104
|
5
|
100
|
66
|
|
|
38
|
if ($data->[0] && ref($data->[0]) eq 'HASH' && !$self->{'collapse'}) { |
|
|
|
|
66
|
|
|
|
|
|
105
|
4
|
|
|
|
|
8
|
@title{keys %$_} = () for grep {ref($_) eq 'HASH'} @$data; # find all uniques |
|
|
12
|
|
|
|
|
56
|
|
|
106
|
4
|
0
|
|
|
|
8
|
my @keys = @{ $self->{'sort'} || [sort {($a eq 'id') ? -1 : ($b eq 'id') ? 1 : $a cmp $b } keys %title] }; |
|
|
4
|
0
|
|
|
|
19
|
|
|
|
0
|
50
|
|
|
|
0
|
|
|
107
|
4
|
|
|
|
|
14
|
$add->(\@keys, \@title); |
|
108
|
4
|
|
|
|
|
18
|
foreach my $row (@$data) { |
|
109
|
12
|
50
|
|
|
|
35
|
if (ref($row) ne 'HASH') { |
|
110
|
0
|
|
|
|
|
0
|
$add->($row, \@bucket); |
|
111
|
0
|
|
|
|
|
0
|
next; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
12
|
|
|
|
|
54
|
$add->([@$row{@keys}], \@bucket); |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
} else { |
|
116
|
1
|
|
|
|
|
2
|
my $title = $self->{'title'}; |
|
117
|
1
|
50
|
|
|
|
4
|
$add->($title, \@title) if $title; |
|
118
|
1
|
|
|
|
|
6
|
$add->([$_], \@bucket) for @$data; |
|
119
|
1
|
50
|
|
|
|
5
|
$add->(['(empty array)'], \@bucket) if !@bucket; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
19
|
|
50
|
|
|
102
|
my $indent = $self->{'indent'} || ''; |
|
124
|
19
|
|
|
|
|
53
|
my $sep = "${indent}$border[8]".join($border[9], map {$border[11] x $_} @max)."$border[10]\n"; |
|
|
41
|
|
|
|
|
148
|
|
|
125
|
19
|
100
|
|
|
|
63
|
my $fmt = "${indent}$border[0]".join($border[1], map {'%'.($dir[$_] ? '-' : '').$max[$_].'s'} 0..$#max)."$border[2]\n"; |
|
|
41
|
|
|
|
|
192
|
|
|
126
|
|
|
|
|
|
|
|
|
127
|
19
|
50
|
33
|
|
|
110
|
if (!$self->{'collapse'} and my $cols = $self->{'auto_collapse'}) { |
|
128
|
0
|
0
|
0
|
|
|
0
|
$cols = $ENV{'COLUMNS'} || eval { die if ! -t STDOUT; require Term::ReadKey; (Term::ReadKey::GetTerminalSize(\*STDOUT))[0] } || 80 if $cols eq '1'; |
|
129
|
0
|
0
|
|
|
|
0
|
if (length($sep) - 1 > $cols) { |
|
130
|
0
|
|
|
|
|
0
|
local $self->{'collapse'} = 1; |
|
131
|
0
|
0
|
|
|
|
0
|
local $self->{'_level'} if $self->{'_level'} == 1; |
|
132
|
0
|
|
|
|
|
0
|
return $self->tablify($data); |
|
133
|
|
|
|
|
|
|
} |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
19
|
|
|
|
|
36
|
my $out = ""; |
|
137
|
19
|
|
|
|
|
39
|
$out .= "${indent}$border[4]".join($border[5], map {$border[7] x $_} @max)."$border[6]\n"; |
|
|
41
|
|
|
|
|
153
|
|
|
138
|
6
|
|
|
6
|
|
79
|
no warnings 'uninitialized'; # because of multiline |
|
|
6
|
|
|
|
|
57
|
|
|
|
6
|
|
|
|
|
2867
|
|
|
139
|
19
|
|
|
|
|
54
|
for my $buck (\@title, \@bucket) { |
|
140
|
38
|
|
|
|
|
75
|
for my $row (@$buck) { |
|
141
|
33
|
50
|
|
|
|
96
|
if (ref $row) { |
|
142
|
33
|
|
|
|
|
154
|
$out .= sprintf($fmt, @$row); |
|
143
|
|
|
|
|
|
|
} else { |
|
144
|
0
|
|
|
|
|
0
|
$out .= sprintf("$border[0]%-*s$border[2]\n", length($sep) - (length($border[0])+length($border[2])+1), $row); |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
} |
|
147
|
38
|
100
|
|
|
|
84
|
if ($buck == \@title) { |
|
148
|
19
|
100
|
|
|
|
59
|
$out .= $sep if @title; |
|
149
|
|
|
|
|
|
|
} else { |
|
150
|
19
|
|
|
|
|
42
|
$out .= "${indent}$border[12]".join($border[13], map {$border[15] x $_} @max)."$border[14]\n"; |
|
|
41
|
|
|
|
|
128
|
|
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
|
|
154
|
19
|
50
|
|
|
|
85
|
utf8::encode($out) if $self->{'_level'} == 1; |
|
155
|
19
|
|
|
|
|
420
|
return $out; |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
1; |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
__END__ |