File Coverage

blib/lib/Term/Output/List/ANSI.pm
Criterion Covered Total %
statement 25 46 54.3
branch 1 8 12.5
condition 1 2 50.0
subroutine 7 8 87.5
pod 2 2 100.0
total 36 66 54.5


line stmt bran cond sub pod time code
1             package Term::Output::List::ANSI;
2 2     2   1611 use 5.020;
  2         8  
3 2     2   1239 use Moo 2;
  2         17043  
  2         15  
4 2     2   4506 use Term::Cap;
  2         7450  
  2         85  
5 2     2   13 use Scalar::Util 'weaken';
  2         4  
  2         122  
6 2     2   13 use experimental 'signatures';
  2         4  
  2         19  
7              
8             our $VERSION = '0.06';
9              
10             =head1 NAME
11              
12             Term::Output::List::ANSI - output an updateable list of ongoing jobs to an ANSI terminal
13              
14             =head1 SYNOPSIS
15              
16             my $printer = Term::Output::List->new(
17             hook_warnings => 1,
18             );
19             my @ongoing_tasks = ('file1: frobnicating', 'file2: bamboozling', 'file3: frobnicating');
20             $printer->output_list(@ongoing_tasks);
21              
22             $printer->output_permanent("Frobnicated gizmos"); # appears above the list
23              
24             =cut
25              
26             =head1 MEMBERS
27              
28             =head2 C<< fh >>
29              
30             Filehandle used for output. Default is C<< STDOUT >>.
31              
32             =cut
33              
34             has 'terminfo' => (
35             is => 'lazy',
36             default => sub { Term::Cap->Tgetent({ OSPEED => 112000 })},
37             );
38              
39             has 'term_scroll_up' => (
40             is => 'lazy',
41             default => sub { $_[0]->terminfo->Tputs('UP') },
42             );
43              
44             has 'term_clear_eol' => (
45             is => 'lazy',
46             default => sub { $_[0]->terminfo->Tputs('ce') },
47             );
48              
49             =head2 C<< interactive >>
50              
51             Whether the script is run interactively and should output intermittent
52             updateable information
53              
54             =head2 C<< hook_warnings >>
55              
56             Install a hook for sending warnings to C<< ->output_permanent >>. This
57             prevents ugly tearing/overwriting when your code outputs warnings.
58              
59             =cut
60              
61             =head1 METHODS
62              
63             =head2 C<< Term::Output::List::ANSI->new() >>
64              
65             my $output = Term::Output::List::ANSI->new(
66             hook_warnings => 1,
67             )
68              
69             =over 4
70              
71             =item C<< hook_warnings >>
72              
73             Install a hook for sending warnings to C<< ->output_permanent >>. This
74             prevents ugly tearing/overwriting when your code outputs warnings.
75              
76             =back
77              
78             =cut
79              
80             =head2 C<< width >>
81              
82             Width of the terminal. This is initialized at first use. You may (or may not)
83             want to set up a C<< $SIG{WINCH} >> handler to set the terminal width when
84             the terminal size changes.
85              
86             =cut
87              
88             has 'width' => (
89             is => 'lazy',
90             default => sub { `tput cols` },
91             );
92              
93             =head1 METHODS
94              
95             =head2 C<< Term::Output::List->new() >>
96              
97             =cut
98              
99             =head2 C<< ->scroll_up >>
100              
101             Helper method to place the cursor at the top of the updateable list.
102              
103             =cut
104              
105             has 'ellipsis' => (
106             is => 'lazy',
107 2     2   2277 default => sub { "\N{HORIZONTAL ELLIPSIS}" },
  2         17581  
  2         12  
108             );
109              
110             with 'Term::Output::List::Role';
111              
112 0     0 1 0 sub scroll_up( $self, $count=$self->_last_lines ) {
  0         0  
  0         0  
  0         0  
113 0 0       0 if( !$count) {
114             } else {
115             # Overwrite the number of lines we printed last time
116 0         0 print { $self->fh } "\r" . sprintf $self->term_scroll_up(), ${count};
  0         0  
117             #sleep 1;
118             };
119             }
120              
121             =head2 C<< ->output_permanent >>
122              
123             $o->output_permanent("Frobnicated 3 items for job 2");
124             $o->output_list("Frobnicating 9 items for job 1",
125             "Frobnicating 2 items for job 3",
126             );
127              
128             Outputs items that should go on the permanent record. It is expected to
129             output the (remaining) list of ongoing jobs after that.
130              
131             =cut
132              
133 10     10 1 167 sub output_permanent( $self, @items ) {
  10         16  
  10         17  
  10         15  
134 10   50     39 my $total = $self->_last_lines // 0;
135 10 50       191 if( !$self->interactive ) {
136 10         107 print { $self->fh } join("\n", @items) . "\n";
  10         177  
137              
138             } else {
139 0           $self->scroll_up($total);
140 0           my $w = $self->width;
141 0           my $clear_eol = $self->term_clear_eol;
142              
143 0 0         if( @items ) {
144              
145 0           print { $self->fh }
146             join("$clear_eol\n",
147 0           map { $self->_trim( $_, $w ) }
148 0           map { s/\s*\z//r }
  0            
149             @items
150             )."$clear_eol\n";
151             };
152              
153             # If we have fewer items than before, clear the lines of the vanished items
154 0           my $blank = $total - @items;
155 0 0         if( $blank > 0 ) {
156 0           print { $self->fh } "$clear_eol\n"x ($blank);
  0            
157 0           $self->scroll_up( $blank );
158             }
159 0           $self->fresh_output();
160             }
161             }
162              
163             =head2 C<< ->output_list @items >>
164              
165             $o->output_list("Frobnicating 9 items for job 1",
166             "Frobnicating 2 items for job 3",
167             );
168              
169             Outputs items that can be updated later, as long as no intervening output
170             (like from C, C or C) has happened. If you want to output
171             lines that should not be overwritten later, see C<output_permanent>>
172              
173             =head2 C<< ->fresh_output >>
174              
175             $o->fresh_output();
176              
177             Helper subroutine to make all items from the last output list remain as is.
178              
179             For compatibility between output to a terminal and output without a terminal,
180             you should use C<< ->output_permanent >> for things that should be permanent
181             instead.
182              
183             =cut
184              
185             1;