File Coverage

blib/lib/Term/FormatColumns.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


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