File Coverage

blib/lib/Pod/Term.pm
Criterion Covered Total %
statement 221 231 95.6
branch 66 98 67.3
condition 41 74 55.4
subroutine 31 31 100.0
pod 4 4 100.0
total 363 438 82.8


line stmt bran cond sub pod time code
1             package Pod::Term;
2              
3 2     2   108166 use strict;
  2         11  
  2         47  
4 2     2   8 use warnings;
  2         3  
  2         37  
5 2     2   1048 use Pod::Simple;
  2         48587  
  2         53  
6 2     2   12 use base 'Pod::Simple';
  2         2  
  2         242  
7 2     2   974 use Term::ANSIColor 'colored';
  2         13099  
  2         1198  
8 2     2   700 use Clone 'clone';
  2         4007  
  2         84  
9 2     2   11 use Carp;
  2         3  
  2         82  
10 2     2   794 use Hash::Merge;
  2         10371  
  2         4417  
11              
12             our $VERSION = 0.01;
13              
14             sub _default_prop_map{
15             return {
16 1     1   41 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   9 max_cols => 76,
164             base_color => 'white'
165             };
166             }
167              
168              
169              
170             sub globals{
171 2483     2483 1 11560 my ($self,$globals) = @_;
172              
173 2483 50 66     3422 confess "Expected a hash ref but got $globals" if defined $globals && ref $globals ne ref {};
174              
175 2483 100       2810 if ( $globals ){
176 16         29 $self->{globals} = $globals;
177             }
178              
179 2483   66     3008 $self->{globals} ||= $self->_default_globals;
180 2483         4413 return $self->{globals};
181             }
182              
183              
184              
185             sub prop_map{
186 6009     6009 1 16560 my ($self,$prop_map) = @_;
187              
188 6009 50 66     8035 confess "Expected a hash ref but got $prop_map" if defined $prop_map && ref $prop_map ne ref {};
189              
190 6009 100       6811 if ( $prop_map ){
191 17         32 $self->{prop_map} = $prop_map;
192             }
193              
194 6009   66     7053 $self->{prop_map} ||= $self->_default_prop_map;
195 6009         7782 return $self->{prop_map};
196             }
197              
198              
199             sub set_props{
200 1     1 1 444 my ($self,$props) = @_;
201              
202 1 50       4 confess "Need a hash ref of properties to set" unless $props;
203 1 50       5 confess "Expected a hash ref but got $props" if ref $props ne ref {};
204              
205 1         10 my $merger = Hash::Merge->new('LEFT_PRECEDENT');
206 1         110 my $prop_map = $merger->merge( $props, $self->prop_map );
207 1         906 $self->prop_map( $prop_map );
208             }
209              
210              
211              
212             sub set_prop{
213 156     156 1 78614 my ($self,$element_name,$prop_name,$value) = @_;
214              
215 156 50 33     671 confess "set_prop needs: element_name, prop_name, value" unless $element_name && $prop_name && $value;
      33        
216 156         255 $self->prop_map->{$element_name}{$prop_name} = $value;
217              
218             }
219              
220              
221             sub _stack{
222 1344     1344   1405 my ($self,$stack) = @_;
223              
224 1344 50 33     1766 confess "Expected an array ref but got $stack" if defined $stack && ref $stack ne ref [];
225              
226 1344 50       1541 if ( $stack ){
227 0         0 $self->{stack} = $stack;
228             }
229              
230 1344   100     1804 $self->{stack} ||= [];
231 1344         2994 return $self->{stack};
232             }
233              
234             sub _color{
235 2016     2016   2182 my ($self,$color) = @_;
236              
237 2016 50 33     2687 confess "Expected a string but got $color" if defined $color && ref $color ne ref '';
238              
239 2016 50       2287 if ( $color ){
240 0         0 $self->{color} = $color
241             }
242 2016   33     3568 $self->{color} ||= $self->globals->{base_color};
243              
244 2016         3099 return $self->{color};
245             }
246              
247             sub _last_color{
248 864     864   930 my ($self,$color) = @_;
249              
250 864 50 33     1187 confess "Expected a string but got $color" if defined $color && ref $color ne ref '';
251              
252 864 50       967 if ( $color ){
253 0         0 $self->{last_color} = $color;
254             }
255              
256 864   33     1584 $self->{last_color} ||= $self->_color;
257 864         1068 return $self->{last_color};
258             }
259              
260              
261             sub _blocks{
262 864     864   972 my ($self,$blocks) = @_;
263              
264 864 50 33     1117 confess "Expected an array ref but got $blocks" if defined $blocks && ref $blocks ne ref [];
265              
266 864 50       979 if ( $blocks ){
267 0         0 $self->{blocks} = $blocks;
268             }
269              
270 864   100     1190 $self->{blocks} ||= [];
271 864         1427 return $self->{blocks};
272             }
273              
274              
275              
276              
277             sub _stack_start{
278 288     288   368 my ($self,$element) = @_;
279              
280 288         351 my $stacking = $self->_get_prop($element,'stacking');
281              
282 288 100       475 if ( $stacking eq 'nest' ){
    100          
    50          
283              
284 208         205 push @{$self->_stack}, $element;
  208         250  
285              
286             } elsif ( $stacking eq 'spot' ){
287              
288 16         14 my @stack = @{$self->_stack};
  16         20  
289 16 50       34 push @{$self->_stack}, $element unless $stack[$#stack] eq $element;
  16         19  
290              
291             } elsif ( $stacking eq 'revert' ){
292              
293 64         89 my @stack = @{$self->_stack};
  64         81  
294 64         125 my ($i) = grep{ $self->_stack->[$_] eq $element } 0..$#stack;
  192         216  
295              
296 64 50       101 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         60 push @{$self->_stack}, $element;
  64         77  
304              
305             }
306             }
307             }
308              
309              
310              
311             sub _stack_end{
312 288     288   346 my ($self, $element) = @_;
313              
314 288         383 my $stacking = $self->prop_map->{ $element }->{ stacking };
315              
316 288 100       674 if ( $stacking eq 'nest' ){
317              
318 208         201 pop @{$self->_stack};
  208         267  
319              
320             }
321             }
322              
323              
324              
325              
326              
327             sub _calc_indent{
328 576     576   542 my $self = shift;
329              
330 576         528 my $indent = 0;
331              
332 576         496 my @stack = @{$self->_stack};
  576         695  
333              
334 576         1024 for my $i (0..$#stack){
335            
336 2512         2684 my $prop_set = $self->prop_map->{ $stack[$i] };
337 2512 100       3323 $indent += $prop_set->{ indent } if $prop_set->{ indent };
338 2512 100 100     3768 $indent += $prop_set->{ after_indent } if $prop_set->{after_indent} && $i != $#stack;
339              
340             }
341              
342 576         865 return $indent;
343             }
344              
345              
346              
347              
348             sub _insert{
349 48     48   71 my ($self,$ins,$block) = @_;
350            
351 48         57 my $text = $ins->{text};
352 48   33     95 $block ||= $self->_blocks->[0];
353              
354 48 50       71 if ( $block->{attr} ){
355              
356 48         105 my @frags = split( /\\\\/, $ins->{text} );
357 48         82 for my $i (0..$#frags){
358              
359 48         76 $frags[$i] =~ s/(?{attr}{$1}}/g;
  0         0  
360              
361             }
362 48         87 $text = join('\\',@frags);
363             }
364              
365 48         97 my $item = { text => $text };
366 48   33     96 my $color = $ins->{color} || $self->_color;
367 48 50       59 $item->{color} = $color if $color;
368 48         48 push @{$block->{items}}, $item;
  48         111  
369              
370             }
371              
372              
373              
374             sub _color_start{
375 288     288   336 my ($self,$element) = @_;
376              
377 288         414 my $att_set = $self->prop_map->{$element};
378              
379 288         439 $self->_last_color( $self->_color );
380 288 50       466 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   337 my ($self,$element) = @_;
390              
391 288         321 my $color_cp = $self->_color;
392 288         399 $self->_color( $self->_last_color );
393 288         360 $self->_last_color( $color_cp );
394              
395             }
396              
397              
398              
399             sub _get_prop{
400 2592     2592   3049 my ($self,$element,$prop_name) = @_;
401              
402 2592         2200 my $prop;
403              
404 2592         2781 my $prop_set = $self->prop_map->{ $element };
405              
406 2592 50       3209 if ( $prop_set ){
407              
408 2592         2708 $prop = $prop_set->{$prop_name};
409              
410             }
411 2592         4330 return $prop;
412             }
413              
414              
415              
416              
417             sub _handle_element_start{
418              
419 288     288   57243 my ($self, $element, $attr) = @_;
420              
421 288         519 $self->_color_start( $element );
422              
423 288         443 my $display = $self->_get_prop( $element, 'display' );
424              
425 288 50 33     693 if ( $display && $display eq 'block' ){
426              
427 288   100     394 my $top_spacing = $self->_get_prop( $element, 'top_spacing' ) || 0;
428 288 100       427 print "\n" x $top_spacing if $top_spacing;
429              
430              
431 288         379 my $indent = $self->_calc_indent;
432              
433 288         493 $self->_stack_start( $element );
434              
435 288         653 my $block = { items => [], indent => $indent, name => $element };
436 288 50       1618 $block->{attr} = clone $attr if $attr;
437 288   100     528 $block->{wrap} = $self->_get_prop( $element, 'wrap' ) || 'normal';
438 288   100     417 $block->{top_spacing} = $self->_get_prop( $element, 'top_spacing' ) || 0;
439 288   100     423 $block->{bottom_spacing} = $self->_get_prop( $element, 'bottom_spacing' ) || 0;
440 288         290 unshift @{$self->_blocks}, $block;
  288         352  
441              
442             }
443              
444 288         390 my $prepend = $self->_get_prop( $element, 'prepend' );
445 288 100       566 $self->_insert( $prepend ) if $prepend;
446              
447             }
448            
449              
450              
451              
452              
453              
454             sub _handle_element_end{
455 288     288   4371 my ($self, $element, $attr) = @_;;
456              
457 288         468 $self->_color_end( $element );
458              
459 288         714 my $append = $self->_get_prop( $element, 'append' );
460 288 50       376 $self->_insert( $append ) if $append;
461              
462 288         383 my $display = $self->_get_prop( $element, 'display' );
463              
464 288 50 33     713 if ( $display && $display eq 'block' ){
465 288         259 my $block = shift @{$self->_blocks};
  288         313  
466              
467 288         371 $block->{indent} = $self->_calc_indent;
468              
469 288 100 66     768 if ( $block->{wrap} && $block->{wrap} eq 'verbatim'){
470 16         23 $self->_print_verbatim( $block );
471             } else {
472 272         397 $self->_print_block( $block );
473             }
474 288 100       571 print "\n" x $block->{bottom_spacing} if $block->{bottom_spacing};
475              
476 288         444 $self->_stack_end( $element );
477             }
478             }
479              
480              
481              
482             sub _handle_text{
483 240     240   1565 my ($self, $text) = @_;
484              
485 240         348 my $item = {
486             text => $text
487             };
488              
489 240 50       313 $item->{color} = $self->_color if $self->_color;
490              
491 240         255 push @{$self->_blocks->[0]->{items}},$item;
  240         276  
492              
493             }
494              
495              
496              
497              
498              
499             sub _print_block{
500 272     272   308 my ($self,$block) = @_;
501              
502 272         1677 my $items = clone $block->{items};
503 272         380 my $in_body = 0;
504              
505 272         455 while (@$items){
506              
507 450         507 my $line = [];
508 450         581 my $chars_left = $self->globals->{max_cols} - $block->{indent};
509              
510 450 50       631 confess "Attempt to print block with an indent >= the maximum number of columns" if $chars_left < 1;
511            
512 450         426 my $item;
513              
514 450         386 do {
515              
516 948         970 $item = shift @$items;
517              
518 948 100       1244 if ( $item ){
519              
520 724 100       963 if ( length( $item->{text} ) <= $chars_left ) {
521              
522 272         330 push @$line, $item;
523 272         387 $chars_left -= length( $item->{text} );
524              
525             } else {
526              
527 452         392 my $q_item;
528 452         585 ($item,$q_item) = $self->_break_item( $item, $chars_left );
529              
530 452 100       801 if ( $item ){
531 226         285 push @$line, $item;
532 226         249 $chars_left -= length( $item->{text} );
533             }
534 452         714 unshift @$items, $q_item;
535              
536             }
537              
538             }
539              
540             } while ( $item );
541              
542 450         614 my $margin = ' ' x $block->{indent};
543 450         744 my $line_str = '';
544              
545 450         554 foreach my $li ( @$line ){
546              
547 498 50 33     1644 if ( $li->{text} !~ /^\s*$/s && $li->{color} ){
548 0         0 $line_str .= colored( $li->{text}, $li->{color} );
549             } else {
550 498         759 $line_str .= $li->{text};
551             }
552             }
553              
554              
555 450         591 $line_str = $margin.$line_str;
556 450 100       780 $line_str = "\n".$line_str if $in_body;
557 450         798 print $line_str;
558            
559 450         997 $in_body = 1;
560              
561             }
562              
563             }
564              
565              
566              
567             sub _print_verbatim{
568 16     16   20 my ($self,$block) = @_;
569              
570 16         17 my $text = '';
571              
572 16         15 my $color;
573 16         17 foreach my $item ( @{$block->{items}} ){
  16         24  
574              
575 16   33     46 $color ||= $item->{color};
576 16         26 $text .= $item->{text};
577              
578             }
579              
580 16 50       49 return if $text =~ /^\s*$/;
581              
582 16   100     35 my $indent = $block->{indent} || 0;
583 16         22 my $margin = ' ' x $indent;
584              
585 16         34 my @lines = split( /\n/,$text );
586 16 50       31 push @lines,"" if $text =~ /\n$/;
587              
588 16         17 $text = '';
589 16         24 for my $i (0..$#lines){
590              
591 16         22 my $line = $lines[$i];
592              
593 16 50       33 if ( $line =~ /^\s*$/ ){
594 0         0 $line = '';
595             } else {
596 16         81 $line =~ s/^(.*)$/$margin$1/;
597             }
598              
599 16         24 $text .= $line;
600 16 50       35 $text .= "\n" unless $i == $#lines;
601             }
602              
603 16 50       32 $text = colored( $text, $color ) if $color;
604 16         36 print $text;
605            
606             }
607              
608              
609              
610              
611              
612             sub _break_item{
613 452     452   557 my ($self,$item,$chars_left) = @_;
614              
615 452         466 my $text = $item->{text};
616 452         443 my $start_length = length( $text );
617              
618 452         396 my $clipped;
619            
620 452 100       572 if ( $chars_left > 1 ){
621              
622 375         3395 $text =~ s/^(.{0,$chars_left})\s+//s;
623              
624 375         890 $clipped = $1;
625              
626             }
627              
628 452         491 my $inc_item = undef;
629 452 100       588 if ( $clipped ){
630              
631             $inc_item = {
632             text => $clipped.' ',
633             color => $item->{color}
634             }
635            
636 226         479 }
637              
638             return ($inc_item,{
639             text => $text,
640             color => $item->{color},
641              
642 452         1021 });
643              
644             }
645              
646             1;
647             __END__