File Coverage

blib/lib/OptArgs2/Pager.pm
Criterion Covered Total %
statement 91 122 74.5
branch 19 54 35.1
condition 15 40 37.5
subroutine 19 24 79.1
pod 11 13 84.6
total 155 253 61.2


line stmt bran cond sub pod time code
1             package OptArgs2::Pager;
2 1     1   143548 use strict;
  1         2  
  1         27  
3 1     1   21 use warnings;
  1         2  
  1         35  
4 1     1   4 use Carp ();
  1         1  
  1         41  
5 1     1   865 use Exporter::Tidy other => [qw/page start_pager stop_pager/];
  1         12  
  1         4  
6 1     1   467 use File::Which;
  1         978  
  1         47  
7 1     1   393 use IO::Handle;
  1         5196  
  1         117  
8             ### START Class::Inline ### v0.0.1 Wed Dec 3 10:44:51 2025
9             require Carp;
10             our ( @_CLASS, $_FIELDS, %_NEW );
11              
12             sub new {
13 2     2 0 6472 my $class = shift;
14 2   33     13 my $CLASS = ref $class || $class;
15 2   66     10 $_NEW{$CLASS} //= do {
16 1         3 my ( %seen, @new, @build );
17 1         4 my @possible = ($CLASS);
18 1         19 while (@possible) {
19 1         4 my $c = shift @possible;
20 1     1   5 no strict 'refs';
  1         2  
  1         1238  
21 1 50       2 push @new, $c . '::_NEW' if exists &{ $c . '::_NEW' };
  1         15  
22 1 50       3 push @build, $c . '::BUILD' if exists &{ $c . '::BUILD' };
  1         6  
23 1         3 $seen{$c}++;
24 1 50       8 if ( exists &{ $c . '::DOES' } ) {
  1         6  
25 0         0 push @possible, grep { not $seen{$_}++ } $c->DOES('*');
  0         0  
26             }
27 1         2 push @possible, grep { not $seen{$_}++ } @{ $c . '::ISA' };
  0         0  
  1         16  
28             }
29 1         9 [ [ reverse(@new) ], [ reverse(@build) ] ];
30             };
31 2 50       12 my $self = { @_ ? @_ > 1 ? @_ : %{ $_[0] } : () };
  0 100       0  
32 2         6 bless $self, $CLASS;
33 2         14 my $attrs = { map { ( $_ => 1 ) } keys %$self };
  1         5  
34 2         13 map { $self->$_($attrs) } @{ $_NEW{$CLASS}->[0] };
  2         11  
  2         9  
35             {
36 2         3 local $Carp::CarpLevel = 3;
  2         6  
37 2         8 Carp::carp("OptArgs2::Pager: unexpected argument '$_'") for keys %$attrs
38             }
39 2         3 map { $self->$_ } @{ $_NEW{$CLASS}->[1] };
  2         9  
  2         7  
40 2         10 $self;
41             }
42              
43             sub _NEW {
44 2     2   4 CORE::state $fix_FIELDS = do {
45 1 50       11 $_FIELDS = { @_CLASS > 1 ? @_CLASS : %{ $_CLASS[0] } };
  0         0  
46 1 50       7 $_FIELDS = $_FIELDS->{'FIELDS'} if exists $_FIELDS->{'FIELDS'};
47             };
48 2         5 map { delete $_[1]->{$_} } 'auto', 'encoding', 'pager';
  6         18  
49             }
50              
51             sub __RO {
52 0     0   0 my ( undef, undef, undef, $sub ) = caller(1);
53 0         0 Carp::confess("attribute $sub is read-only");
54             }
55 2 50 66 2 1 8 sub auto { __RO() if @_ > 1; $_[0]{'auto'} //= $_FIELDS->{'auto'}->{'default'} }
  2         27  
56              
57             sub encoding {
58 0 0   0 1 0 __RO() if @_ > 1;
59 0   0     0 $_[0]{'encoding'} //= $_FIELDS->{'encoding'}->{'default'};
60             }
61              
62             sub fh {
63 34 100   34 1 109 if ( @_ > 1 ) { $_[0]{'fh'} = $_[1] }
  2         5  
64 34   66     199 $_[0]{'fh'} //= $_FIELDS->{'fh'}->{'default'}->( $_[0] );
65             }
66              
67             sub orig_fh {
68 16 50   16 1 51 __RO() if @_ > 1;
69 16   66     97 $_[0]{'orig_fh'} //= $_FIELDS->{'orig_fh'}->{'default'}->( $_[0] );
70             }
71              
72             sub pager {
73 0 0   0 1 0 __RO() if @_ > 1;
74 0   0     0 $_[0]{'pager'} //= $_FIELDS->{'pager'}->{'default'}->( $_[0] );
75             }
76              
77             sub pid {
78 0 0   0 1 0 if ( @_ > 1 ) { $_[0]{'pid'} = $_[1] }
  0         0  
79 0   0     0 $_[0]{'pid'} // undef;
80             }
81             @_CLASS = grep 1, ### END Class::Inline ###
82             # User provided arguments
83             auto => { default => 1, },
84             encoding => { default => ':utf8', },
85             pager => { default => \&_build_pager, },
86              
87             # Attributes
88             fh => {
89             init_arg => undef,
90             is => 'rw',
91             default => sub { IO::Handle->new },
92             },
93             orig_fh => {
94             init_arg => undef,
95             default => sub { select },
96             },
97             pid => {
98             init_arg => undef,
99             is => 'rw',
100             init_arg => undef,
101             },
102             ;
103              
104             our $VERSION = 'v2.0.17';
105             our @CARP_NOT = (__PACKAGE__);
106              
107             sub _build_pager {
108 0     0   0 my $self = shift;
109              
110 0 0       0 if ( exists $ENV{PAGER} ) {
111 0 0       0 return unless length( $ENV{PAGER} );
112              
113             # Explicit pager defined
114 0         0 my ( $pager, @options ) = split ' ', $ENV{PAGER};
115 0         0 my $path = File::Which::which($pager);
116 0 0       0 Carp::croak("pager not found: $pager") unless $path;
117 0         0 return join( ' ', $path, @options );
118             }
119              
120             # Otherwise take the first from our own list
121 0         0 foreach my $pager (qw/pager less most w3m lv pg more/) {
122 0         0 my $path = File::Which::which($pager);
123 0 0       0 return $path if $path;
124             }
125              
126 0         0 Carp::croak("no suitable pager found");
127             }
128              
129             sub BUILD {
130 2     2 0 4 my $self = shift;
131 2         8 $self->open;
132 2 50       9 select $self->fh if $self->auto;
133             }
134              
135             sub open {
136 7     7 1 12 my $self = shift;
137              
138 7 100       17 return $self->fh if $self->fh->opened;
139              
140 2 50       73 if ( not -t $self->orig_fh ) {
141 2         7 $self->fh( $self->orig_fh );
142 2         5 return;
143             }
144              
145 0   0     0 my $pager = $self->pager || return;
146              
147 0   0     0 local $ENV{LESS} = $ENV{LESS} // '-FSXeR';
148 0 0 0     0 local $ENV{MORE} = $ENV{MORE} // '-FXer' unless $^O eq 'MSWin32';
149              
150 0 0       0 $self->pid( CORE::open( $self->fh, '|-', $pager ) )
151             or Carp::croak "Could not pipe to PAGER ('$pager'): $!\n";
152              
153 0 0       0 binmode( $self->fh, $self->encoding ? $self->encoding : () )
    0          
154             or Carp::cluck "Could not set bindmode: $!";
155              
156 0         0 $self->fh->autoflush(1);
157             }
158              
159             sub close {
160 4     4 1 10 my $self = shift;
161 4 50 33     15 return unless $self->fh && $self->fh->opened;
162              
163 4         40 select $self->orig_fh;
164 4 50       13 $self->fh->close if $self->fh ne $self->orig_fh;
165             }
166              
167             sub DESTROY {
168 1     1   3 my $self = shift;
169 1         4 $self->close;
170             }
171              
172             # Functions
173             my $pager;
174              
175             sub start_pager {
176 3   66 3 1 17124 $pager //= __PACKAGE__->new(@_);
177 3         11 $pager->open;
178 3         8 select $pager->fh;
179             }
180              
181             sub stop_pager {
182 4   100 4 1 192110 $pager // return;
183 3         11 $pager->close;
184 3         7 select( $pager->orig_fh );
185             }
186              
187             sub page {
188 2     2 1 6447 my $text = shift;
189 2         5 my $close = not $pager;
190              
191 2   33     11 $pager //= __PACKAGE__->new( @_, auto => 0 );
192 2         7 $pager->open;
193 2         7 my $ok = $pager->fh->printflush($text);
194 2 50       271 $pager->close if $close;
195 2         10 $ok;
196             }
197              
198             1;
199              
200             __END__