File Coverage

blib/lib/Pod/Term.pm
Criterion Covered Total %
statement 223 235 94.8
branch 67 100 67.0
condition 43 77 55.8
subroutine 31 31 100.0
pod 4 4 100.0
total 368 447 82.3


line stmt bran cond sub pod time code
1             package Pod::Term;
2              
3 2     2   178889 use strict;
  2         17  
  2         71  
4 2     2   15 use warnings;
  2         6  
  2         85  
5 2     2   1467 use Pod::Simple;
  2         87810  
  2         89  
6 2     2   20 use base 'Pod::Simple';
  2         6  
  2         338  
7 2     2   1489 use Term::ANSIColor 'colored';
  2         23789  
  2         1972  
8 2     2   1180 use Clone 'clone';
  2         6315  
  2         128  
9 2     2   21 use Carp;
  2         6  
  2         144  
10 2     2   1170 use Hash::Merge;
  2         17113  
  2         8008  
11              
12             our $VERSION = 0.02;
13              
14             sub _default_prop_map{
15             return {
16 1     1   68 head1 => {
17             display => 'block',
18             stacking => 'revert',
19             indent => 0,
20             after_indent => 2,
21             color => 'on_blue',
22             bottom_spacing => 2
23             },
24              
25             head2 => {
26             display => 'block',
27             stacking => 'revert',
28             indent => 0,
29             after_indent => 2,
30             color => 'blue',
31             bottom_spacing => 2
32             },
33              
34             head3 => {
35             display => 'block',
36             stacking => 'revert',
37             indent => 0,
38             after_indent => 2,
39             color => 'magenta',
40             bottom_spacing => 2
41             },
42              
43             head4 => {
44             display => 'block',
45             stacking => 'revert',
46             indent => 0,
47             after_indent => 2,
48             color => 'bright_magenta',
49             bottom_spacing => 2
50             },
51              
52             'over-text' => {
53             display => 'block',
54             stacking => 'nest',
55             indent => 2
56             },
57              
58             'over-number' => {
59             display => 'block',
60             stacking => 'nest',
61             indent => 2
62             },
63              
64             'over-bullet' => {
65             display => 'block',
66             stacking => 'nest',
67             indent => 2,
68             bottom_spacing => 1
69             },
70              
71             'item-text' => {
72             display => 'block',
73             stacking => 'spot',
74             color => 'yellow',
75             indent => 0,
76             after_indent => 2,
77             bottom_spacing => 2
78             },
79              
80             'item-number' => {
81             display => 'block',
82             stacking => 'nest',
83             color => 'yellow',
84             prepend => {
85             text => '@number. ',
86             color => 'red'
87             },
88             bottom_spacing => 2
89             },
90              
91             'item-bullet' => {
92             display => 'block',
93             stacking => 'nest',
94             color => 'yellow',
95             prepend => {
96             text => '* ',
97             color => 'red'
98             },
99             bottom_spacing => 1
100             },
101              
102             'B' => {
103             display => 'inline',
104             color => 'bright_yellow'
105             },
106              
107             'C' => {
108             display => 'inline',
109             color => 'cyan'
110             },
111              
112             'I' => {
113             display => 'inline',
114             color => 'bright_white'
115             },
116              
117             'L' => {
118             display => 'inline',
119             color => 'bright_green'
120             },
121              
122             'E' => {
123             display => 'inline',
124             color => 'white'
125             },
126              
127             'F' => {
128             display => 'inline',
129             color => 'bright_white'
130             },
131              
132             'S' => {
133             display => 'inline',
134             color => 'cyan',
135             wrap => 'verbatim'
136             },
137              
138             'Para' => {
139             display => 'block',
140             stacking => 'nest',
141             color => 'white',
142             bottom_spacing => 2,
143             },
144              
145             'Verbatim' => {
146             display => 'block',
147             stacking => 'nest',
148             color => 'cyan',
149             bottom_spacing => 2,
150             wrap => 'verbatim'
151             },
152              
153             'Document' => {
154             display => 'block',
155             stacking => 'nest',
156             indent => 2
157             }
158             };
159             }
160              
161             sub _default_globals {
162             return {
163 1     1   15 max_cols => 76,
164             base_color => 'white'
165             };
166             }
167              
168              
169              
170             sub globals{
171 2483     2483 1 22956 my ($self,$globals) = @_;
172              
173 2483 50 66     6635 confess "Expected a hash ref but got $globals" if defined $globals && ref $globals ne ref {};
174              
175 2483 100       5944 if ( $globals ){
176 16         59 $self->{globals} = $globals;
177             }
178              
179 2483   66     6464 $self->{globals} ||= $self->_default_globals;
180 2483         9414 return $self->{globals};
181             }
182              
183              
184              
185             sub prop_map{
186 6009     6009 1 24283 my ($self,$prop_map) = @_;
187              
188 6009 50 66     16291 confess "Expected a hash ref but got $prop_map" if defined $prop_map && ref $prop_map ne ref {};
189              
190 6009 100       13630 if ( $prop_map ){
191 17         74 $self->{prop_map} = $prop_map;
192             }
193              
194 6009   66     15360 $self->{prop_map} ||= $self->_default_prop_map;
195 6009         16526 return $self->{prop_map};
196             }
197              
198              
199             sub set_props{
200 1     1 1 649 my ($self,$props) = @_;
201              
202 1 50       6 confess "Need a hash ref of properties to set" unless $props;
203 1 50       9 confess "Expected a hash ref but got $props" if ref $props ne ref {};
204              
205 1         13 my $merger = Hash::Merge->new('LEFT_PRECEDENT');
206 1         150 my $prop_map = $merger->merge( $props, $self->prop_map );
207 1         1539 $self->prop_map( $prop_map );
208             }
209              
210              
211              
212             sub set_prop{
213 156     156 1 138587 my ($self,$element_name,$prop_name,$value) = @_;
214              
215 156 50 33     1135 confess "set_prop needs: element_name, prop_name, value" unless $element_name && $prop_name && $value;
      33        
216 156         481 $self->prop_map->{$element_name}{$prop_name} = $value;
217              
218             }
219              
220              
221             sub _stack{
222 1344     1344   2989 my ($self,$stack) = @_;
223              
224 1344 50 33     3521 confess "Expected an array ref but got $stack" if defined $stack && ref $stack ne ref [];
225              
226 1344 50       3080 if ( $stack ){
227 0         0 $self->{stack} = $stack;
228             }
229              
230 1344   100     3939 $self->{stack} ||= [];
231 1344         6028 return $self->{stack};
232             }
233              
234             sub _color{
235 2016     2016   4489 my ($self,$color) = @_;
236              
237 2016 50 33     5188 confess "Expected a string but got $color" if defined $color && ref $color ne ref '';
238              
239 2016 50       4931 if ( $color ){
240 0         0 $self->{color} = $color
241             }
242 2016   33     7874 $self->{color} ||= $self->globals->{base_color};
243              
244 2016         6655 return $self->{color};
245             }
246              
247             sub _last_color{
248 864     864   2039 my ($self,$color) = @_;
249              
250 864 50 33     2341 confess "Expected a string but got $color" if defined $color && ref $color ne ref '';
251              
252 864 50       2052 if ( $color ){
253 0         0 $self->{last_color} = $color;
254             }
255              
256 864   33     3556 $self->{last_color} ||= $self->_color;
257 864         2363 return $self->{last_color};
258             }
259              
260              
261             sub _blocks{
262 864     864   1981 my ($self,$blocks) = @_;
263              
264 864 50 33     2297 confess "Expected an array ref but got $blocks" if defined $blocks && ref $blocks ne ref [];
265              
266 864 50       2135 if ( $blocks ){
267 0         0 $self->{blocks} = $blocks;
268             }
269              
270 864   100     2363 $self->{blocks} ||= [];
271 864         3095 return $self->{blocks};
272             }
273              
274              
275              
276              
277             sub _stack_start{
278 288     288   686 my ($self,$element) = @_;
279              
280 288         762 my $stacking = $self->_get_prop($element,'stacking');
281              
282 288 100       990 if ( $stacking eq 'nest' ){
    100          
    50          
283              
284 208         377 push @{$self->_stack}, $element;
  208         541  
285              
286             } elsif ( $stacking eq 'spot' ){
287              
288 16         40 my @stack = @{$self->_stack};
  16         49  
289 16 50       70 push @{$self->_stack}, $element unless $stack[$#stack] eq $element;
  16         49  
290              
291             } elsif ( $stacking eq 'revert' ){
292              
293 64         129 my @stack = @{$self->_stack};
  64         184  
294 64         231 my ($i) = grep{ $self->_stack->[$_] eq $element } 0..$#stack;
  192         483  
295              
296 64 50       231 if ( defined $i ){
297              
298 0         0 my @new_stack = @stack[0..$i];
299 0         0 $self->_stack( \@new_stack );
300              
301             } else {
302              
303 64         160 push @{$self->_stack}, $element;
  64         169  
304              
305             }
306             }
307             }
308              
309              
310              
311             sub _stack_end{
312 288     288   795 my ($self, $element) = @_;
313              
314 288         820 my $stacking = $self->prop_map->{ $element }->{ stacking };
315              
316 288 100       1287 if ( $stacking eq 'nest' ){
317              
318 208         379 pop @{$self->_stack};
  208         530  
319              
320             }
321             }
322              
323              
324              
325              
326              
327             sub _calc_indent{
328 576     576   1205 my $self = shift;
329              
330 576         1178 my $indent = 0;
331              
332 576         964 my @stack = @{$self->_stack};
  576         1601  
333              
334 576         1986 for my $i (0..$#stack){
335            
336 2512         6152 my $prop_set = $self->prop_map->{ $stack[$i] };
337 2512 100       6596 $indent += $prop_set->{ indent } if $prop_set->{ indent };
338 2512 100 100     7957 $indent += $prop_set->{ after_indent } if $prop_set->{after_indent} && $i != $#stack;
339              
340             }
341              
342 576         2039 return $indent;
343             }
344              
345              
346              
347              
348             sub _insert{
349 48     48   128 my ($self,$ins,$block) = @_;
350            
351 48         117 my $text = $ins->{text};
352 48   33     195 $block ||= $self->_blocks->[0];
353              
354 48 50       156 if ( $block->{attr} ){
355              
356 48         201 my @frags = split( /\\\\/, $ins->{text} );
357 48         194 for my $i (0..$#frags){
358              
359 48         164 $frags[$i] =~ s/(?{attr}{$1}}/g;
  0         0  
360              
361             }
362 48         171 $text = join('\\',@frags);
363             }
364              
365 48         164 my $item = { text => $text };
366 48   33     213 my $color = $ins->{color} || $self->_color;
367 48 50       138 $item->{color} = $color if $color;
368 48         97 push @{$block->{items}}, $item;
  48         237  
369              
370             }
371              
372              
373              
374             sub _color_start{
375 288     288   690 my ($self,$element) = @_;
376              
377 288         786 my $att_set = $self->prop_map->{$element};
378              
379 288         850 $self->_last_color( $self->_color );
380 288 50       966 if ( $att_set->{color} ){
381 0         0 $self->_color( $att_set->{color} );
382             }
383             }
384              
385              
386            
387              
388             sub _color_end{
389 288     288   644 my ($self,$element) = @_;
390              
391 288         726 my $color_cp = $self->_color;
392 288         818 $self->_color( $self->_last_color );
393 288         802 $self->_last_color( $color_cp );
394              
395             }
396              
397              
398              
399             sub _get_prop{
400 2592     2592   6711 my ($self,$element,$prop_name) = @_;
401              
402 2592         4456 my $prop;
403              
404 2592         5848 my $prop_set = $self->prop_map->{ $element };
405              
406 2592 50       6945 if ( $prop_set ){
407              
408 2592         5735 $prop = $prop_set->{$prop_name};
409              
410             }
411 2592         8648 return $prop;
412             }
413              
414              
415              
416              
417             sub _handle_element_start{
418              
419 288     288   108908 my ($self, $element, $attr) = @_;
420              
421 288         1111 $self->_color_start( $element );
422              
423 288         832 my $display = $self->_get_prop( $element, 'display' );
424              
425 288 50 33     1340 if ( $display && $display eq 'block' ){
426              
427 288   100     890 my $top_spacing = $self->_get_prop( $element, 'top_spacing' ) || 0;
428 288 100       885 print "\n" x $top_spacing if $top_spacing;
429              
430              
431 288         793 my $indent = $self->_calc_indent;
432              
433 288         1067 $self->_stack_start( $element );
434              
435 288         1299 my $block = { items => [], indent => $indent, name => $element };
436 288 50       3095 $block->{attr} = clone $attr if $attr;
437 288   100     1060 $block->{wrap} = $self->_get_prop( $element, 'wrap' ) || 'normal';
438 288   100     863 $block->{top_spacing} = $self->_get_prop( $element, 'top_spacing' ) || 0;
439 288   100     842 $block->{bottom_spacing} = $self->_get_prop( $element, 'bottom_spacing' ) || 0;
440 288         577 unshift @{$self->_blocks}, $block;
  288         767  
441              
442             }
443              
444 288         812 my $prepend = $self->_get_prop( $element, 'prepend' );
445 288 100       1175 $self->_insert( $prepend ) if $prepend;
446              
447             }
448            
449              
450              
451              
452              
453              
454             sub _handle_element_end{
455 288     288   8507 my ($self, $element, $attr) = @_;;
456              
457 288         951 $self->_color_end( $element );
458              
459 288         777 my $append = $self->_get_prop( $element, 'append' );
460 288 50       719 $self->_insert( $append ) if $append;
461              
462 288         814 my $display = $self->_get_prop( $element, 'display' );
463              
464 288 50 33     1357 if ( $display && $display eq 'block' ){
465 288         567 my $block = shift @{$self->_blocks};
  288         715  
466              
467 288         770 $block->{indent} = $self->_calc_indent;
468              
469 288 100 66     1452 if ( $block->{wrap} && $block->{wrap} eq 'verbatim'){
470 16         60 $self->_print_verbatim( $block );
471             } else {
472 272         828 $self->_print_block( $block );
473             }
474 288 100       1186 print "\n" x $block->{bottom_spacing} if $block->{bottom_spacing};
475              
476 288         1034 $self->_stack_end( $element );
477             }
478             }
479              
480              
481              
482             sub _handle_text{
483 240     240   3136 my ($self, $text) = @_;
484              
485 240         768 my $item = {
486             text => $text
487             };
488              
489 240 50       660 $item->{color} = $self->_color if $self->_color;
490              
491 240         480 push @{$self->_blocks->[0]->{items}},$item;
  240         582  
492              
493             }
494              
495              
496              
497              
498              
499             sub _print_block{
500 272     272   647 my ($self,$block) = @_;
501              
502 272         3122 my $items = clone $block->{items};
503 272         727 my $in_body = 0;
504              
505 272         916 while (@$items){
506              
507 450         1069 my $line = [];
508 450         1306 my $max_chars = $self->globals->{max_cols} - $block->{indent};
509 450         892 my $chars_left = $max_chars;
510              
511 450 50       1155 confess "Attempt to print block with an indent >= the maximum number of columns" if $chars_left < 1;
512            
513 450         797 my $item;
514              
515 450         814 do {
516              
517 948         1937 $item = shift @$items;
518              
519 948 100       2500 if ( $item ){
520              
521 724 100       1926 if ( length( $item->{text} ) <= $chars_left ) {
522              
523 272         634 push @$line, $item;
524 272         774 $chars_left -= length( $item->{text} );
525              
526             } else {
527              
528 452         852 my $q_item;
529 452         1262 ($item,$q_item) = $self->_break_item( $item, $chars_left, $max_chars );
530              
531 452 100       1638 if ( $item ){
532 226         582 push @$line, $item;
533 226         529 $chars_left -= length( $item->{text} );
534             }
535 452         1536 unshift @$items, $q_item;
536              
537             }
538              
539             }
540              
541             } while ( $item );
542              
543 450         1189 my $margin = ' ' x $block->{indent};
544 450         846 my $line_str = '';
545              
546 450         1066 foreach my $li ( @$line ){
547              
548 498 50 33     3033 if ( $li->{text} !~ /^\s*$/s && $li->{color} ){
549 0         0 $line_str .= colored( $li->{text}, $li->{color} );
550             } else {
551 498         1447 $line_str .= $li->{text};
552             }
553             }
554              
555              
556 450         1304 $line_str = $margin.$line_str;
557 450 100       1449 $line_str = "\n".$line_str if $in_body;
558 450         1499 print $line_str;
559            
560 450         2147 $in_body = 1;
561              
562             }
563              
564             }
565              
566              
567              
568             sub _print_verbatim{
569 16     16   46 my ($self,$block) = @_;
570              
571 16         42 my $text = '';
572              
573 16         34 my $color;
574 16         32 foreach my $item ( @{$block->{items}} ){
  16         56  
575              
576 16   33     91 $color ||= $item->{color};
577 16         54 $text .= $item->{text};
578              
579             }
580              
581 16 50       88 return if $text =~ /^\s*$/;
582              
583 16   100     78 my $indent = $block->{indent} || 0;
584 16         57 my $margin = ' ' x $indent;
585              
586 16         72 my @lines = split( /\n/,$text );
587 16 50       66 push @lines,"" if $text =~ /\n$/;
588              
589 16         54 $text = '';
590 16         65 for my $i (0..$#lines){
591              
592 16         45 my $line = $lines[$i];
593              
594 16 50       82 if ( $line =~ /^\s*$/ ){
595 0         0 $line = '';
596             } else {
597 16         172 $line =~ s/^(.*)$/$margin$1/;
598             }
599              
600 16         53 $text .= $line;
601 16 50       84 $text .= "\n" unless $i == $#lines;
602             }
603              
604 16 50       72 $text = colored( $text, $color ) if $color;
605 16         82 print $text;
606            
607             }
608              
609              
610              
611              
612              
613             sub _break_item{
614 452     452   1170 my ($self,$item,$chars_left, $max_chars) = @_;
615              
616 452         971 my $text = $item->{text};
617 452         858 my $start_length = length( $text );
618              
619 452         786 my $clipped;
620            
621 452 100       1212 if ( $chars_left > 1 ){
622              
623 375         6617 $text =~ s/^(.{0,$chars_left})\s+//s;
624              
625 375         1704 $clipped = $1;
626              
627             }
628              
629 452 50 66     1797 if (! $clipped && $chars_left == $max_chars ){
630 0         0 $text =~ s/^(.{$chars_left})//s;
631 0         0 $clipped = $1;
632             }
633              
634 452         907 my $inc_item = undef;
635 452 100       1041 if ( $clipped ){
636              
637             $inc_item = {
638             text => $clipped.' ',
639             color => $item->{color}
640             }
641            
642 226         998 }
643              
644             return ($inc_item,{
645             text => $text,
646             color => $item->{color},
647              
648 452         2002 });
649              
650             }
651              
652             1;
653             __END__