File Coverage

blib/lib/Markdown/Pod/Handler.pm
Criterion Covered Total %
statement 148 193 76.6
branch 18 26 69.2
condition 1 3 33.3
subroutine 42 54 77.7
pod 0 43 0.0
total 209 319 65.5


line stmt bran cond sub pod time code
1             package Markdown::Pod::Handler;
2             # ABSTRACT: Parser module to convert from markdown to POD
3              
4 31     31   150 use strict;
  31         43  
  31         1259  
5 31     31   148 use warnings;
  31         50  
  31         1655  
6              
7             our $VERSION = '0.006';
8              
9 31         439 use Markdent::Types qw(
10             Bool Str HashRef OutputStream HeaderLevel
11 31     31   138 );
  31         49  
12              
13 31     31   220413 use namespace::autoclean;
  31         57  
  31         268  
14 31     31   2129 use Moose;
  31         44  
  31         257  
15 31     31   168904 use MooseX::SemiAffordanceAccessor;
  31         58  
  31         295  
16 31     31   89994 use MooseX::Params::Validate qw( validated_list validated_hash );
  31         60  
  31         307  
17 31     31   7724 use List::Util;
  31         57  
  31         5179  
18              
19             with 'Markdent::Role::EventsAsMethods';
20              
21             has encoding => (
22             is => 'ro',
23             isa => Str,
24             default => q{},
25             );
26              
27             has _output => (
28             is => 'ro',
29             isa => OutputStream,
30             required => 1,
31             init_arg => 'output',
32             );
33              
34             # Default width for horizontal rule
35             #
36             our $HORIZONTAL_RULE_WIDTH = 80;
37              
38             my $link_buf;
39             my $code_buf;
40             my $tble_buf;
41             my @tble = ( [] );
42             my @blockquotes;
43             my @list_type;
44              
45             use constant {
46 31         77236 STACK_LINK => 1,
47             STACK_CODE => 2,
48             STACK_TBLE => 3,
49             STACK_STRONG => 4,
50             STACK_EMPHASIS => 5,
51 31     31   169 };
  31         47  
52              
53             my @style_stack;
54              
55             sub _stream {
56 7714     7714   13346 my ( $self, @params ) = @_;
57 7714         7279 print { $self->_output } @params;
  7714         224145  
58             }
59              
60             sub start_document {
61 30     30 0 51153 my $self = shift;
62              
63 30 50       976 $self->_stream( '=encoding ' . $self->encoding . "\n\n" ) if $self->encoding;
64             }
65              
66 30     30 0 5677 sub end_document { }
67              
68             sub text {
69 4085     4085 0 6523883 my $self = shift;
70 4085         15560 my ($text) = validated_list( \@_, text => { isa => Str } );
71              
72 4085 100       2569595 if (@style_stack) {
73             # This allows the end_link() handler to know that *some* text was inside
74             # it. So if one has [`text`](http://example.org/), the end_code()
75             # handler will output the code to the stream before the end_link()
76             # finishes.
77 1503 100       2983 $link_buf->{text} = '' if grep { $_ == STACK_LINK } @style_stack;
  1508         6522  
78              
79 1503 100       4537 if ( $style_stack[-1] == STACK_LINK ) {
    100          
    50          
80 598         2862 $link_buf->{text} = $text;
81             }
82             elsif ( $style_stack[-1] == STACK_CODE ) {
83 728         3609 $code_buf->{text} = $text;
84             }
85             elsif ( $style_stack[-1] == STACK_TBLE ) {
86 0         0 $tble_buf->{text} = $text;
87             }
88             else {
89             # another kind of style that does not require storing state
90 177         545 $self->_stream($text);
91             }
92             }
93             else {
94 2582         7534 $self->_stream($text);
95             }
96             }
97              
98             sub start_header {
99 253     253 0 147850 my $self = shift;
100 253         1351 my ($level) = validated_list( \@_, level => { isa => HeaderLevel }, );
101              
102 253         246959 $self->_stream("\n=head$level ");
103             }
104              
105             sub end_header {
106 253     253 0 74877 my $self = shift;
107 253         1310 my ($level) = validated_list( \@_, level => { isa => HeaderLevel }, );
108              
109 253         245952 $self->_stream("\n");
110             }
111              
112             sub start_paragraph {
113 830     830 0 781929 my $self = shift;
114             }
115              
116             sub end_paragraph {
117 830     830 0 292236 my $self = shift;
118              
119 830         2118 $self->_stream("\n");
120             }
121              
122             sub start_link {
123 601     601 0 2022921 my $self = shift;
124 601         2874 my %p = validated_hash(
125             \@_,
126             uri => { isa => Str },
127             title => { isa => Str, optional => 1 },
128             id => { isa => Str, optional => 1 },
129             is_implicit_id => { isa => Bool, optional => 1 },
130             );
131              
132 601         1027884 delete @p{ grep { !defined $p{$_} } keys %p };
  1673         3080  
133              
134 601         1255 push @style_stack, STACK_LINK;
135 601         1565 $link_buf->{uri} = $p{uri};
136 601         1739 $self->_stream('L<');
137             }
138              
139             sub end_link {
140 601     601 0 29070 my $self = shift;
141              
142 601 50 33     4268 if ( $link_buf && exists $link_buf->{text} ) {
143 601         2997 $self->_stream("$link_buf->{text}|$link_buf->{uri}>");
144             }
145             else {
146 0         0 $self->_stream("$link_buf->{uri}>");
147             }
148              
149 601         1074 pop @style_stack;
150 601         2056 $link_buf = undef;
151             }
152              
153             sub start_strong {
154 13     13 0 11443 my $self = shift;
155              
156 13         30 push @style_stack, STACK_STRONG;
157 13         76 $self->_stream('B<');
158             }
159              
160             sub end_strong {
161 13     13 0 948 my $self = shift;
162              
163 13         21 pop @style_stack;
164 13         35 $self->_stream('>');
165             }
166              
167             sub start_emphasis {
168 166     166 0 175225 my $self = shift;
169              
170 166         384 push @style_stack, STACK_EMPHASIS;
171 166         516 $self->_stream('I<');
172             }
173              
174             sub end_emphasis {
175 166     166 0 13615 my $self = shift;
176              
177 166         337 pop @style_stack;
178 166         435 $self->_stream('>');
179             }
180              
181             sub preformatted {
182 257     257 0 152089 my $self = shift;
183 257         1255 my ($text) = validated_list( \@_, text => { isa => Str }, );
184              
185 257         189134 chomp $text;
186 257         3818 $text =~ s/^/ /gsm;
187 257         966 $self->_stream( $text, "\n\n" );
188             }
189              
190             sub start_blockquote {
191 1     1 0 698 my $self = shift;
192              
193 1         5 $self->_stream("=over 2\n\n");
194             }
195              
196             sub end_blockquote {
197 1     1 0 417 my $self = shift;
198              
199 1         6 $self->_stream("=back\n\n");
200             }
201              
202             sub start_unordered_list {
203 82     82 0 46622 my $self = shift;
204              
205 82         266 $self->_stream("=over\n\n");
206             }
207              
208             sub end_unordered_list {
209 82     82 0 12852 my $self = shift;
210              
211 82         228 $self->_stream("=back\n\n");
212             }
213              
214             sub start_ordered_list {
215 3     3 0 4630 my $self = shift;
216              
217 3         8 $self->_stream("=over\n\n");
218             }
219              
220             sub end_ordered_list {
221 3     3 0 392 my $self = shift;
222              
223 3         7 $self->_stream("=back\n\n");
224             }
225              
226             sub start_list_item {
227 379     379 0 82894 my $self = shift;
228 379         1577 my %p = validated_hash( \@_, bullet => { isa => Str }, );
229              
230 379         245109 $self->_stream("=item $p{bullet}\n\n");
231             }
232              
233             sub end_list_item {
234 379     379 0 123441 my $self = shift;
235              
236 379         881 $self->_stream("\n\n");
237             }
238              
239             sub start_code {
240 728     728 0 1183088 my $self = shift;
241             # Start buffering this snippet
242 728         1317 push @style_stack, STACK_CODE;
243 728         2650 $code_buf = {};
244             }
245              
246             sub end_code {
247 728     728 0 58594 my $self = shift;
248 728         1318 my $text = $code_buf->{'text'};
249 728 50       1999 if ( $text =~ /\n/m ) {
250             # Multi-line. Probably code block
251             #
252 0         0 $text =~ s/^(.*)$/ $1/mg;
253 0         0 $self->_stream($text);
254             }
255             else {
256             # Single line
257             #
258 728 100       2157 if ( $text =~ /[<>]/ ) {
259             # this is so that extra angle brackets are not used unless necessary
260 17         165 my @all_angle = $text =~ /(<+|>+)/g;
261 17         41 my @all_angle_len = map { length $_ } @all_angle;
  34         87  
262 17         137 my $longest = List::Util::max @all_angle_len;
263              
264 17         98 my $start_angle = "<" x ( $longest + 2 );
265 17         42 my $end_angle = ">" x ( $longest + 2 );
266 17         91 $self->_stream("C$start_angle $text $end_angle");
267             }
268             else {
269 711         2702 $self->_stream("C<$text>");
270             }
271             }
272 728         1258 pop @style_stack;
273 728         2429 $code_buf = undef;
274             }
275              
276             sub code_block {
277 0     0 0 0 my $self = shift;
278 0         0 my ($code) = validated_list(
279             \@_,
280             code => { isa => Str },
281             language => { isa => Str, optional => 1 }
282             );
283 0         0 $code =~ s/^(.*)$/ $1/mg;
284 0         0 $self->_stream("\n$code\n");
285             }
286              
287             sub image {
288 74     74 0 639968 my $self = shift;
289 74         495 my %p = validated_hash(
290             \@_,
291             alt_text => { isa => Str },
292             uri => { isa => Str, optional => 1 },
293             title => { isa => Str, optional => 1 },
294             id => { isa => Str, optional => 1 },
295             is_implicit_id => { isa => Bool, optional => 1 },
296             );
297              
298 74         255889 delete @p{ grep { !defined $p{$_} } keys %p };
  329         558  
299              
300 74 50       461 my $alt_text = exists $p{alt_text} ? qq|alt="$p{alt_text}"| : q{};
301              
302 74 100       277 my $attr = exists $p{title} ? $p{title} : q{};
303 74         132 my $attr_text = q{};
304 74         331 while ( $attr =~ s/(\S+)="(.*?)"// ) {
305 0         0 $attr_text .= qq{ $1="$2"};
306             }
307 74         432 while ( $attr =~ /(\S+)=(\S+)/g ) {
308 33         234 $attr_text .= qq{ $1="$2"};
309             }
310              
311 74         510 $self->_stream(qq|=for html <img src="$p{uri}" $alt_text$attr_text />|);
312             }
313              
314             sub start_html_tag {
315 8     8 0 906 my $self = shift;
316 8         42 my ( $tag, $attributes ) = validated_list(
317             \@_,
318             tag => { isa => Str },
319             attributes => { isa => HashRef },
320             );
321             }
322              
323             sub end_html_tag {
324 5     5 0 1532 my $self = shift;
325 5         24 my ( $tag, $attributes ) = validated_list( \@_, tag => { isa => Str }, );
326             }
327              
328             sub html_tag {
329 37     37 0 4441 my $self = shift;
330 37         190 my ( $tag, $attributes ) = validated_list(
331             \@_,
332             tag => { isa => Str },
333             attributes => { isa => HashRef },
334             );
335              
336 37         57721 my $attributes_str = q{};
337 0         0 $attributes_str = join q{ },
338 37         192 map { qq|$_="$attributes->{$_}"| } sort keys %$attributes;
339 37 50       230 if ( $tag =~ /^br$/i ) {
340 37         212 $self->_stream(qq|<$tag $attributes_str />\n|);
341             }
342             else {
343 0         0 $self->_stream(qq|<$tag $attributes_str />|);
344             }
345             }
346              
347             sub html_block {
348 2     2 0 740 my $self = shift;
349 2         8 my ($html) = validated_list( \@_, html => { isa => Str }, );
350              
351 2         1789 chomp $html;
352 2         58 $self->_output()->print(
353             <<"END_HTML"
354              
355             =begin html
356              
357             $html
358              
359             =end html
360              
361             END_HTML
362             );
363             }
364              
365             sub line_break {
366 2     2 0 122 my $self = shift;
367 2         7 $self->_stream("\n\n");
368             }
369              
370             sub html_entity {
371 1     1 0 78 my $self = shift;
372 1         5 my ($entity) = validated_list( \@_, entity => { isa => Str } );
373              
374 1         1290 $self->_stream("E<$entity>");
375             }
376              
377             # Added A.Speer
378             sub horizontal_rule {
379 0     0 0   my $self = shift;
380 0           $self->_stream( ( '=' x $HORIZONTAL_RULE_WIDTH ) . "\n" );
381             }
382              
383             sub auto_link {
384 0     0 0   my $self = shift;
385 0           my ($uri) = validated_list( \@_, uri => { isa => Str } );
386 0           $self->_stream("L<$uri>");
387             }
388              
389             sub html_comment_block {
390 0     0 0   my $self = shift;
391             # Stub
392             }
393              
394             sub start_table {
395 0     0 0   my $self = shift;
396             # Stub
397             }
398              
399             sub start_table_body {
400 0     0 0   my $self = shift;
401             # Stub
402             }
403              
404             sub start_table_row {
405 0     0 0   my $self = shift;
406             # Stub
407             }
408              
409             sub start_table_cell {
410 0     0 0   my $self = shift;
411 0           push @style_stack, STACK_TBLE;
412 0           $tble_buf = {};
413             }
414              
415             sub end_table {
416 0     0 0   my $self = shift;
417 0 0         eval {
418 0           require Text::Table::Tiny;
419 0           1;
420             }
421             || die('unable to load Text::Table::Tiny - please make sure it is installed !');
422 0           my $table =
423             Text::Table::Tiny::table( rows => \@tble, separate_rows => 0, header_row => 0 );
424             # Indent so table appears as POD code. Open to other suggestions
425 0           $table =~ s/^(.*)/ $1/mg;
426 0           $table .= "\n";
427             # Safety in case parser skips end-cell - which it seems to do sometimes
428 0           pop @style_stack;
429 0           $tble_buf = undef;
430 0           $self->_stream($table);
431             }
432              
433             sub end_table_body {
434 0     0 0   my $self = shift;
435             # Safety
436 0           pop @style_stack;
437 0           $tble_buf = undef;
438             }
439              
440             sub end_table_row {
441 0     0 0   my $self = shift;
442 0           push @tble, [];
443             # Safety
444 0           pop @style_stack;
445 0           $tble_buf = undef;
446             }
447              
448             sub end_table_cell {
449 0     0 0   my $self = shift;
450 0           push @{ $tble[$#tble] }, $tble_buf->{'text'};
  0            
451             # Stop buffering table text
452 0           pop @style_stack;
453 0           $tble_buf = undef;
454             }
455              
456             __PACKAGE__->meta->make_immutable;
457 31     31   274 no Moose;
  31         46  
  31         221  
458             1;
459              
460             __END__
461              
462             =pod
463              
464             =encoding UTF-8
465              
466             =head1 NAME
467              
468             Markdown::Pod::Handler - Parser module to convert from markdown to POD
469              
470             =head1 VERSION
471              
472             version 0.006
473              
474             =head1 SYNOPSIS
475              
476             my $handler = Markdown::Pod::Handler->new(
477             encoding => $encoding,
478             output => $fh,
479             );
480            
481             my $parser = Markdent::Parser->new(
482             dialect => $dialect,
483             handler => $handler,
484             );
485              
486             =head1 DESCRIPTION
487              
488             This module is a handler of L<Markdent> Markdown parser.
489             It converts Markdown to POD.
490              
491             =head1 ATTRIBUTES
492              
493             =head2 markdown
494              
495             markdown text
496              
497             =head2 encoding
498              
499             encoding to use
500              
501             =head1 METHODS
502              
503             =head2 new
504              
505             create Markdown::Pod::Handler object
506              
507             =head2 markdown_to_pod
508              
509             convert markdown text to POD text
510              
511             =for Pod::Coverage STACK_CODE
512             STACK_EMPHASIS
513             STACK_LINK
514             STACK_STRONG
515             STACK_TBLE
516             auto_link
517             code_block
518             end_blockquote
519             end_code
520             end_document
521             end_emphasis
522             end_header
523             end_html_tag
524             end_link
525             end_list_item
526             end_ordered_list
527             end_paragraph
528             end_strong
529             end_table
530             end_table_body
531             end_table_cell
532             end_table_row
533             end_unordered_list
534             horizontal_rule
535             html_block
536             html_comment_block
537             html_entity
538             html_tag
539             image
540             line_break
541             preformatted
542             start_blockquote
543             start_code
544             start_document
545             start_emphasis
546             start_header
547             start_html_tag
548             start_link
549             start_list_item
550             start_ordered_list
551             start_paragraph
552             start_strong
553             start_table
554             start_table_body
555             start_table_cell
556             start_table_row
557             start_unordered_list
558             text
559              
560             =head1 SEE ALSO
561              
562             =over
563              
564             =item *
565              
566             L<Markdent>
567              
568             =item *
569              
570             L<Pod::Markdown>
571              
572             =item *
573              
574             L<Text::MultiMarkdown>, L<Text::Markdown>
575              
576             =back
577              
578             =head1 AUTHOR
579              
580             김도형 - Keedi Kim <keedi@cpan.org>
581              
582             =head1 COPYRIGHT AND LICENSE
583              
584             This software is copyright (c) 2015 by Keedi Kim.
585              
586             This is free software; you can redistribute it and/or modify it under
587             the same terms as the Perl 5 programming language system itself.
588              
589             =cut