File Coverage

blib/lib/Term/FormatColumns.pm
Criterion Covered Total %
statement 51 58 87.9
branch 2 4 50.0
condition n/a
subroutine 11 13 84.6
pod 3 3 100.0
total 67 78 85.9


line stmt bran cond sub pod time code
1             package Term::FormatColumns;
2             # ABSTRACT: Format lists of data into columns across the terminal's width
3             $Term::FormatColumns::VERSION = '0.007';
4 2     2   14456 use strict;
  2         4  
  2         69  
5 2     2   7 use warnings;
  2         3  
  2         70  
6              
7 2         14 use Sub::Exporter -setup => [
8             exports => (
9             qw/format_columns format_columns_for_fh format_columns_for_width/,
10             ),
11 2     2   1019 ];
  2         52938  
12              
13 2     2   1746 use Term::ReadKey qw( GetTerminalSize );
  2         6126  
  2         126  
14 2     2   11 use List::Util qw( max );
  2         2  
  2         155  
15 2     2   950 use List::MoreUtils qw( part each_arrayref );
  2         287881  
  2         19  
16 2     2   2784 use POSIX qw( ceil );
  2         13885  
  2         125  
17 2     2   3476 use Symbol qw(qualify_to_ref);
  2         1865  
  2         1007  
18              
19             # Find the length of a string as displayed on the terminal, ignoring any ANSI
20             # escape sequences.
21             sub _term_length {
22 60     60   51 my ( $str ) = @_;
23 60         70 $str =~ s/\x1b\[[0-9;]+m//g;
24 60         89 return length $str;
25             }
26              
27              
28             sub format_columns {
29 0     0 1 0 return format_columns_for_fh( \*STDOUT, @_ );
30             }
31              
32              
33             sub format_columns_for_fh(*@) {
34 0     0 1 0 my $fh = qualify_to_ref( shift, caller );
35 0         0 my @data = @_;
36              
37             # If we're not attached to a terminal, one column, seperated by newlines
38 0 0       0 if ( !-t $fh ) {
39 0         0 return join "\n", @data, '';
40             }
41              
42             # We're attached to a terminal, print column-wise alphabetically to fit the
43             # terminal width
44 0         0 my ( $term_width, undef, undef, undef ) = GetTerminalSize();
45 0         0 return format_columns_for_width( $term_width, @data );
46             }
47              
48              
49             sub format_columns_for_width {
50 7     7 1 1439 my ( $term_width, @data ) = @_;
51 7         14 my $max_width = max map { _term_length( $_ ) } @data;
  41         46  
52 7         13 $max_width += 2; # make sure at least two spaces between data values
53 7         42 my $columns = int( $term_width / $max_width );
54 7 100       17 if ( $columns <= 1 ) {
55             # Only one column, let the terminal handle things
56 1         6 return join "\n", @data, ''; # Add a \n to the end
57             }
58 6         7 my $output = '';
59 6         7 my $column_width = int( $term_width / $columns );
60 6         28 my $rows = ceil( @data / $columns );
61 6         18 push @data, ('') x ($rows * $columns - @data); # Pad data with empty strings
62 6     36   43 my @index = part { int( $_ / $rows ) } 0..$#data;
  36         47  
63 6         52 my $iter = each_arrayref @index;
64 6         37 while ( my @row_vals = $iter->() ) {
65 17         17 my @cells = map { $data[$_] } @row_vals;
  36         50  
66 17         17 my $last_cell = pop @cells;
67 17         19 for (@cells) {
68 19         26 my $length = _term_length( $_ );
69 19         25 $output .= $_;
70 19         34 $output .= ' ' x ($column_width - $length);
71             }
72 17         56 $output .= $last_cell . "\n";
73             }
74 6         39 return $output;
75             }
76              
77             1;
78              
79             __END__