File Coverage

blib/lib/Markdent/Parser/BlockParser.pm
Criterion Covered Total %
statement 295 306 96.4
branch 73 92 79.3
condition 17 26 65.3
subroutine 48 48 100.0
pod 1 1 100.0
total 434 473 91.7


line stmt bran cond sub pod time code
1             package Markdent::Parser::BlockParser;
2              
3 35     35   241 use strict;
  35         81  
  35         961  
4 35     35   175 use warnings;
  35         72  
  35         766  
5 35     35   175 use namespace::autoclean;
  35         74  
  35         229  
6              
7             our $VERSION = '0.39';
8              
9 35     35   21669 use Digest::SHA qw( sha1_hex );
  35         107722  
  35         3165  
10 35     35   830 use Encode qw( encode );
  35         9151  
  35         1799  
11 35     35   15960 use Markdent::Event::StartDocument;
  35         145  
  35         2006  
12 35     35   20785 use Markdent::Event::EndDocument;
  35         140  
  35         1405  
13 35     35   19628 use Markdent::Event::StartBlockquote;
  35         133  
  35         1384  
14 35     35   19534 use Markdent::Event::EndBlockquote;
  35         146  
  35         1350  
15 35     35   19239 use Markdent::Event::StartHeader;
  35         142  
  35         1475  
16 35     35   17871 use Markdent::Event::EndHeader;
  35         158  
  35         1557  
17 35     35   20543 use Markdent::Event::StartListItem;
  35         149  
  35         1601  
18 35     35   20297 use Markdent::Event::EndListItem;
  35         153  
  35         1400  
19 35     35   19562 use Markdent::Event::StartOrderedList;
  35         135  
  35         1396  
20 35     35   19184 use Markdent::Event::EndOrderedList;
  35         146  
  35         1512  
21 35     35   19462 use Markdent::Event::StartParagraph;
  35         135  
  35         1379  
22 35     35   19575 use Markdent::Event::EndParagraph;
  35         147  
  35         1391  
23 35     35   19703 use Markdent::Event::StartUnorderedList;
  35         191  
  35         1432  
24 35     35   19530 use Markdent::Event::EndUnorderedList;
  35         145  
  35         1379  
25 35     35   19727 use Markdent::Event::HorizontalRule;
  35         131  
  35         2120  
26 35     35   19048 use Markdent::Event::HTMLBlock;
  35         164  
  35         1474  
27 35     35   20757 use Markdent::Event::HTMLCommentBlock;
  35         159  
  35         1535  
28 35     35   20470 use Markdent::Event::Preformatted;
  35         151  
  35         1598  
29 35     35   19073 use Markdent::Regexes qw( :block $HTMLComment );
  35         116  
  35         6299  
30 35     35   293 use Markdent::Types;
  35         83  
  35         335  
31              
32 35     35   859413 use Moose;
  35         99  
  35         390  
33 35     35   244889 use MooseX::SemiAffordanceAccessor;
  35         89  
  35         327  
34 35     35   126987 use MooseX::StrictConstructor;
  35         91  
  35         297  
35              
36             with 'Markdent::Role::BlockParser';
37              
38             has __html_blocks => (
39             traits => ['Hash'],
40             is => 'ro',
41             isa => t( 'HashRef', of => t('Str') ),
42             default => sub { {} },
43             init_arg => undef,
44             handles => {
45             _save_html_block => 'set',
46             _get_html_block => 'get',
47             },
48             );
49              
50             has _list_level => (
51             traits => ['Counter'],
52             is => 'rw',
53             isa => t('Int'),
54             default => 0,
55             init_arg => undef,
56             handles => {
57             '_inc_list_level' => 'inc',
58             '_dec_list_level' => 'dec',
59             },
60             );
61              
62             has _list_item_is_paragraph => (
63             traits => ['Bool'],
64             is => 'ro',
65             isa => t('Bool'),
66             default => 0,
67             init_arg => undef,
68             handles => {
69             _treat_list_item_as_paragraph => 'set',
70             _treat_list_item_as_line => 'unset',
71             },
72             );
73              
74             sub parse_document {
75 178     178 1 362 my $self = shift;
76 178         346 my $text = shift;
77              
78 178         7008 $self->_treat_list_item_as_line;
79              
80 178         756 $self->_hash_html_blocks($text);
81              
82 178         5404 $self->_span_parser->extract_link_ids($text);
83              
84 178         696 $self->_parse_text($text);
85             }
86              
87             {
88              
89             # Stolen from Text::Markdown, along with the whole "extract and replace
90             # with hash" concept.
91             my $block_names_re = qr{
92             p | div | h[1-6] | blockquote | pre | table |
93             dl | ol | ul | script | noscript | form |
94             fieldset | iframe | math | ins | del
95             }xi;
96              
97             sub _hash_html_blocks {
98 178     178   352 my $self = shift;
99 178         294 my $text = shift;
100              
101 178         288 ${$text} =~ s{
  178         3538  
102             ( $BlockStart )
103             (
104             ^ < ($block_names_re) [^>]* >
105             (?s: .+? )
106             (?: </ \3 > \n )+ # This catches repetitions of the final closing block
107             )
108             $BlockEnd
109 14   100     1830 }
110             { ( $1 || q{} ) . $self->_hash_and_save_html($2) }egxm;
111              
112             # We need to treat <hr/> tags as blocks as well, but they don't have
113 178         9717 # an ending delimiter.
  178         1575  
114             ${$text} =~ s{
115             ( $BlockStart )
116 0   0     0 (<hr.*\ */?>)
117             }
118 178         3990 { ( $1 || q{} ) . $self->_hash_and_save_html($2) }egxm;
119              
120             return;
121             }
122             }
123 14     14   35  
124 14         40 sub _hash_and_save_html {
125             my $self = shift;
126 14         98 my $html = shift;
127              
128 14         2565 my $sha1 = lc sha1_hex( encode( 'UTF-8', $html ) );
129              
130 14         6752 $self->_save_html_block( $sha1 => $html );
131              
132             return 'html:' . $sha1 . "\n";
133             }
134 908     908   1392  
135 908         1190 sub _parse_text {
136             my $self = shift;
137 908         1258 my $text = shift;
138              
139 908         1208 my $last_pos;
140 2259 50 33     59438 PARSE:
  0         0  
141             while (1) {
142 0         0 if ( $self->debug && pos ${$text} ) {
  0         0  
  0         0  
143             $self->_print_debug( "Remaining text:\n[\n"
144             . substr( ${$text}, pos ${$text} )
145             . "\n]\n" );
146 2259 100       3403 }
  2259         8771  
147 906         2751  
148             if ( ${$text} =~ / \G \p{Space}* \z /xgc ) {
149             last;
150 1353   100     2307 }
151 1353 50 33     3439  
152 0         0 my $current_pos = pos ${$text} || 0;
153             if ( defined $last_pos && $last_pos == $current_pos ) {
154 0         0 my $msg
155 0         0 = "About to enter an endless loop (pos = $current_pos)!\n";
  0         0  
156 0         0 $msg .= "\n";
157             $msg .= substr( ${$text}, $last_pos );
158 0         0 $msg .= "\n";
159              
160             die $msg;
161 1353         4547 }
162              
163 1353         5348 my @look_for = $self->_possible_block_matches;
164              
165 1353         2971 $self->_debug_look_for(@look_for);
166 11765         145024  
167             for my $block (@look_for) {
168 11765 100       29313 my $meth = '_match_' . $block;
169              
170             $self->$meth($text)
171             and next PARSE;
172 620   50     3537 }
173              
174             $last_pos = pos ${$text} || 0;
175             }
176             }
177 1353     1353   2056  
178             sub _possible_block_matches {
179 1353         2085 my $self = shift;
180              
181 1353 100       36881 my @look_for;
182              
183             push @look_for, qw( hashed_html horizontal_rule )
184 1353         3503 unless $self->_list_level;
185              
186             push @look_for, qw(
187             html_comment
188             atx_header
189             two_line_header
190             blockquote
191             preformatted
192             list
193 1353 100       33079 );
194              
195             push @look_for, 'list_item'
196 1353         2544 if $self->_list_level;
197              
198 1353         4635 push @look_for, 'paragraph';
199              
200             return @look_for;
201             }
202              
203 1256     1256   1984 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
204 1256         1789 sub _match_hashed_html {
205             my $self = shift;
206 1256 100       1804 my $text = shift;
  1256         11373  
207              
208             return unless ${$text} =~ / \G
209             $BlockStart
210             ^
211             (
212             html:([0-9a-f]{40})
213             \n
214             )
215             $BlockEnd
216 14         587 /xmgc;
217              
218 14 50       90 my $html = $self->_get_html_block($2);
219              
220 14 50       382 return unless defined $html;
221              
222             $self->_debug_parse_result(
223             $1,
224             'hashed html',
225 14         69 ) if $self->debug;
226              
227             $self->_send_event(
228             HTMLBlock => html => $html,
229 14         1437 );
230              
231             return 1;
232             }
233 1326     1326   1991  
234 1326         1799 sub _match_html_comment {
235             my $self = shift;
236 1326 100       1809 my $text = shift;
  1326         10072  
237              
238             return unless ${$text} =~ / \G
239             $EmptyLine*?
240             ^
241             \p{SpaceSeparator}{0,3}
242             $HTMLComment
243             $HorizontalWS*
244             \n
245 3         12 /xmgc;
246              
247 3 50       89 my $comment = $1;
248              
249             $self->_debug_parse_result(
250             $comment,
251             'html comment block',
252 3         17 ) if $self->debug;
253              
254 3         12 $self->_detab_text( \$comment );
255              
256 3         263 $self->_send_event( HTMLCommentBlock => text => $comment );
257              
258             return 1;
259             }
260              
261             my $AtxHeader = qr/ ^
262             (\#{1,6})
263             (
264             $HorizontalWS*
265             \S
266             .+?
267             )
268             (?:
269             $HorizontalWS*
270             \#+
271             )?
272             \n
273             /xm;
274 1323     1323   2095  
275 1323         2029 sub _match_atx_header {
276             my $self = shift;
277 1323 100       1950 my $text = shift;
  1323         9702  
278              
279             return unless ${$text} =~ / \G
280             (?:$EmptyLines)?
281             ($AtxHeader)
282 43         408 /xmgc;
283 43         184  
284             my $level = length $2;
285 43 50       1245 my $header_text = $3 . "\n";
286              
287             $self->_debug_parse_result(
288             $1,
289             'atx header',
290             [ level => $level ],
291 43         437 ) if $self->debug;
292              
293 43         1188 $header_text =~ s/^$HorizontalWS*//;
294              
295 43         366 $self->_header( $level, $header_text );
296              
297             return 1;
298             }
299              
300             my $TwoLineHeader = qr/ ^
301             (
302             $HorizontalWS*
303             \S # must have some non-ws
304             .+ # anything else
305             \n
306             )
307             ^(=+|-+) # underline marking a header
308             \n
309             /xm;
310 1280     1280   1935  
311 1280         1802 sub _match_two_line_header {
312             my $self = shift;
313 1280 100       1785 my $text = shift;
  1280         9968  
314              
315             return unless ${$text} =~ / \G
316             (?:$EmptyLines)?
317             ($TwoLineHeader)
318 19 100       1609 /xmgc;
319              
320 19 50       695 my $level = substr( $3, 0, 1 ) eq '=' ? 1 : 2;
321              
322             $self->_debug_parse_result(
323             $1,
324             'two-line header',
325             [ level => $level ],
326 19         93 ) if $self->debug;
327              
328 19         148 $self->_header( $level, $2 );
329              
330             return 1;
331             }
332 62     62   135  
333 62         141 sub _header {
334 62         134 my $self = shift;
335             my $level = shift;
336 62         259 my $text = shift;
337              
338 62         1887 $self->_send_event( StartHeader => level => $level );
339              
340 62         366 $self->_span_parser->parse_block($text);
341              
342 62         1661 $self->_send_event( EndHeader => level => $level );
343              
344             return 1;
345             }
346              
347             my $HorizontalRule = qr/ ^
348             (
349             \p{SpaceSeparator}{0,3}
350             (?:
351             (?: \* \p{SpaceSeparator}? ){3,}
352             |
353             (?: - \p{SpaceSeparator}? ){3,}
354             |
355             (?: _ \p{SpaceSeparator}? ){3,}
356             )
357             \n
358             )
359             /xm;
360 1242     1242   2175  
361 1242         1873 sub _match_horizontal_rule {
362             my $self = shift;
363 1242 100       1783 my $text = shift;
  1242         12676  
364              
365             return unless ${$text} =~ / \G
366             (?:$EmptyLines)?
367             $HorizontalRule
368 6 50       639 /xmgc;
369              
370             $self->_debug_parse_result(
371             $1,
372             'horizontal rule',
373 6         27 ) if $self->debug;
374              
375 6         781 $self->_send_event('HorizontalRule');
376              
377             return 1;
378             }
379 1261     1261   1952  
380 1261         1904 sub _match_blockquote {
381             my $self = shift;
382 1261 100       1674 my $text = shift;
  1261         13817  
383              
384             return unless ${$text} =~ / \G
385             $BlockStart
386             (
387             ^
388             >
389             $HorizontalWS*
390             \S
391             (?:
392             .*
393             \n
394             )+?
395             )
396             (?=
397             $EmptyLine # ... an empty line
398             ^
399             (?=
400             \S # ... followed by content in column 1
401             )
402             (?! # ... which is not
403             > # ... a blockquote
404             $HorizontalWS*
405             \S
406             )
407             |
408             \s* # or end of the document
409             \z
410             )
411 21         763 /xmgc;
412              
413 21 50       634 my $bq = $1;
414              
415             $self->_debug_parse_result(
416             $bq,
417             'blockquote',
418 21         97 ) if $self->debug;
419              
420 21         673 $self->_send_event('StartBlockquote');
421              
422             $bq =~ s/^>$HorizontalWS?//gxm;
423              
424 21         855 # Even if the blockquote is inside a list, we want to look for paragraphs,
425 21         704 # not list items.
426             my $list_level = $self->_list_level;
427             $self->_set_list_level(0);
428              
429             # Dingus treats a new blockquote level as starting a new paragraph as
430 21         171 # well. If we treat each change of blockquote level as starting a new
431             # sub-document, we get the same behavior.
432             for my $chunk (
433 29         118 $self->_split_chunks_on_regex( $bq, qr/^>$HorizontalWS*\S/xm ) ) {
434              
435             $self->_parse_text( \$chunk );
436 21         829 }
437              
438 21         77 $self->_set_list_level($list_level);
439              
440 21         696 $self->_send_event('EndBlockquote');
441              
442             return 1;
443             }
444             ## use critic
445 21     21   234  
446 21         43 sub _split_chunks_on_regex {
447 21         35 my $self = shift;
448             my $text = shift;
449 21         40 my $regex = shift;
450              
451 21         40 my @chunks;
452             my @chunk;
453 21         113 my $in_regex = 0;
454 57         72  
455             for my $line ( split /\n/, $text ) {
456 57 100 66     403 my $new_chunk;
    100 66        
457 3         9  
458 3         5 if ( $in_regex && $line !~ $regex ) {
459             $in_regex = 0;
460             $new_chunk = 1;
461 5         15 }
462 5         12 elsif ( $line =~ $regex && !$in_regex ) {
463             $in_regex = 1;
464             $new_chunk = 1;
465 57 100       125 }
466 8 50       31  
  10         41  
467             if ($new_chunk) {
468 8         23 push @chunks, join q{}, map { $_ . "\n" } @chunk
469             if @chunk;
470             @chunk = ();
471 57         148 }
472              
473             push @chunk, $line;
474 21 50       86 }
  47         157  
475              
476             push @chunks, join q{}, map { $_ . "\n" } @chunk
477 21         75 if @chunk;
478              
479             return @chunks;
480             }
481              
482             my $PreLine = qr/ ^
483             (?:
484             \p{spaceSeparator}{4,}
485             |
486             \t
487             )
488             $HorizontalWS*
489             \S
490             .*
491             \n
492             /xm;
493              
494 1240     1240   2054 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
495 1240         1878 sub _match_preformatted {
496             my $self = shift;
497 1240 100       1711 my $text = shift;
  1240         14018  
498              
499             return unless ${$text} =~ / \G
500             $BlockStart
501             (
502             (?:
503             $PreLine
504             (?:$EmptyLine)*
505             )*
506             $PreLine
507             )
508 142         1907 /xmgc;
509              
510 142 50       4538 my $pre = $1;
511              
512             $self->_debug_parse_result(
513             $pre,
514             'preformatted',
515 142         1392 ) if $self->debug;
516              
517 142         860 $pre =~ s/^(?:\p{SpaceSeparator}{4}|\t)//gm;
518              
519 142         691 $self->_detab_text( \$pre );
520              
521 142         13579 $self->_send_event( Preformatted => text => $pre );
522              
523             return 1;
524             }
525             ## use critic
526              
527             my $Bullet = qr/ (?:
528             \p{SpaceSeparator}{0,3}
529             (
530             [\+\*\-] # unordered list bullet
531             |
532             \d+\. # ordered list number
533             )
534             )
535             $HorizontalWS+
536             /xm;
537 2068     2068   2872  
538             sub _list_re {
539 2068         2699 my $self = shift;
540              
541 2068 100       55027 my $block_start;
542 98         528  
543             if ( $self->_list_level ) {
544             $block_start = qr/(?: (?<= \n ) | $EmptyLines )/xm;
545 1970         9446 }
546             else {
547             $block_start = qr/ $BlockStart /xm;
548 2068         15777 }
549              
550             my $list = qr/ $block_start
551             (
552             $Bullet
553             (?: .* \n )+?
554             )
555 2068         29874 /xm;
556              
557             return $list;
558             }
559              
560 1098     1098   1743 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
561 1098         1625 sub _match_list {
562             my $self = shift;
563 1098         2317 my $text = shift;
564              
565 1098 100       1824 my $list_re = $self->_list_re;
  1098         15828  
566              
567             return unless ${$text} =~ / \G
568             $list_re
569             (?= # list ends with
570             $EmptyLine # ... an empty line
571             (?:
572             (?=
573             $HorizontalRule # ... followed by a horizontal rule
574             )
575             |
576             (?=
577             \S # ... or followed by content in column 1
578             )
579             (?! # ... which is not
580             $Bullet # ... a bullet
581             )
582             )
583             |
584             \s* # or end of the document
585             \z
586             )
587 39         17326 /xmgc;
588 39         95  
589             my $list = $1;
590 39 100       157 my $bullet = $2;
591              
592 39 50       1096 my $type = $bullet =~ /\d/ ? 'OrderedList' : 'UnorderedList';
593              
594             $self->_debug_parse_result(
595             $list,
596             $type,
597 39         191 ) if $self->debug;
598              
599 39         1122 $self->_send_event( 'Start' . $type );
600              
601 39         113 $self->_inc_list_level;
602              
603 39         156 my @items = $self->_split_list_items($list);
604              
605 39         1283 $self->_handle_list_items( $type, @items );
606              
607 39         165 $self->_dec_list_level;
608              
609 39         1077 $self->_send_event( 'End' . $type );
610              
611             return 1;
612             }
613             ## use critic
614 39     39   105  
615 39         79 sub _split_list_items {
616             my $self = shift;
617 39         76 my $list = shift;
618              
619             my @items;
620 39         197 my @chunk;
621              
622 113 100 100     848 for my $line ( split /\n/, $list ) {
623             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
624 36         108 if ( $line =~ /^$Bullet/ && @chunk ) {
  65         200  
625             ## use critic
626 36         90 push @items, join q{}, map { $_ . "\n" } @chunk;
627              
628             @chunk = ();
629 113         1976 }
630              
631             push @chunk, $line;
632 39 50       129 }
  48         151  
633              
634             push @items, join q{}, map { $_ . "\n" } @chunk
635 39         118 if @chunk;
636              
637             return @items;
638             }
639 39     39   80  
640 39         66 sub _handle_list_items {
641 39         92 my $self = shift;
642             my $type = shift;
643 39         67 my @items = @_;
644 39         70  
645 75         601 my $ordinal_list_num = 1;
646             for my $item (@items) {
647             $item =~ s/^$Bullet//;
648 75 100       1724  
649             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
650             my $bullet
651             = $type eq 'OrderedList' ? ( $ordinal_list_num++ ) . q{.} : $1;
652 75         284 ## use critic
653              
654             $self->_send_event( StartListItem => bullet => $bullet );
655              
656             # This strips out indentation from any lines beyond the first. This
657 75         2057 # causes the block parser to see a sub-list as starting a new list
658             # when it parses the entire item for blocks.
659 75 50       1971 $item =~ s/(?<=\n)^ (?: \p{SpaceSeparator}{4} | \t )//xgm;
660              
661             $self->_print_debug("Parsing list item for blocks:\n[$item]\n")
662             if $self->debug;
663              
664             # This is a hack to ensure that the last item in a loose list (each
665 75 100       493 # item is a paragraph) also is treated as a paragraph, not just a list
    100          
666 39 100 100     419 # item.
667             if ( $item eq $items[-1] ) {
668             if ( @items > 1
669 5 50       120 && $items[-2] =~ /^$EmptyLine\z/m ) {
670              
671             $self->_print_debug(
672             "Treating last list item as a paragraph because previous item ends with empty line\n"
673 5         181 ) if $self->debug;
674              
675             $self->_treat_list_item_as_paragraph;
676 34         1947 }
677             else {
678             $self->_treat_list_item_as_line;
679             }
680 6 50       140 }
681             elsif ( $item =~ /^$EmptyLine\z/m ) {
682             $self->_print_debug(
683             "Treating item as a paragraph because it ends with empty line\n"
684 6         230 ) if $self->debug;
685              
686             $self->_treat_list_item_as_paragraph;
687 30         1744 }
688             else {
689             $self->_treat_list_item_as_line;
690 75         317 }
691              
692 75         224 $self->_parse_text( \$item );
693              
694             $self->_send_event('EndListItem');
695             }
696             }
697              
698             # A list item matches multiple lines of text without any separating
699             # newlines. These lines stop when we see a blockquote or indented list
700             # bullet. This match is only done inside a list, and lets us distinguish
701             # between list items which contain paragraphs and those which don't.
702             #
703 78     78   146 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
704 78         120 sub _match_list_item {
705             my $self = shift;
706 78 100       101 my $text = shift;
  78         836  
707              
708             return unless ${$text} =~ / \G
709             ((?:
710             ^
711             \p{SpaceSeparator}*
712             \S
713             .*
714             \n
715             )+?)
716             (?=
717             ^
718             $Bullet
719             |
720             ^
721             > \p{SpaceSeparator}*
722             \S
723             .*
724             \n
725             |
726             \z
727             )
728 68 50       4363 /xmgc;
729              
730             $self->_debug_parse_result(
731             $1,
732             'list_item',
733 68 100       1809 ) if $self->debug;
734              
735             $self->_send_event('StartParagraph')
736 68         1637 if $self->_list_item_is_paragraph;
737              
738 68 100       1746 $self->_span_parser->parse_block($1);
739              
740             $self->_send_event('EndParagraph')
741 68         448 if $self->_list_item_is_paragraph;
742              
743             return 1;
744             }
745 970     970   1655  
746 970         1446 sub _match_paragraph {
747             my $self = shift;
748 970         2179 my $text = shift;
749              
750             my $list_re = $self->_list_re;
751 970 100       1604  
  970         18342  
752             # At this point anything that is not an empty line must be a paragraph.
753             return unless ${$text} =~ / \G
754             (?:$EmptyLines)?
755             ((?:
756             ^
757             $HorizontalWS*
758             \S
759             .*
760             \n
761             )+?)
762             (?:
763             $BlockEnd
764             |
765             (?= $HorizontalRule )
766             |
767             (?= $TwoLineHeader )
768             |
769             (?= $AtxHeader )
770             |
771             (?= $list_re )
772             )
773 350 50       46501 /xmgc;
774              
775             $self->_debug_parse_result(
776             $1,
777             'paragraph',
778 350         1583 ) if $self->debug;
779              
780 348         10439 $self->_send_event('StartParagraph');
781              
782 348         2107 $self->_span_parser->parse_block($1);
783              
784 348         9743 $self->_send_event('EndParagraph');
785              
786             return 1;
787             }
788             ## use critic
789              
790             __PACKAGE__->meta->make_immutable;
791              
792             1;
793              
794             # ABSTRACT: Block parser for standard Markdown
795              
796             __END__
797              
798             =pod
799              
800             =encoding UTF-8
801              
802             =head1 NAME
803              
804             Markdent::Parser::BlockParser - Block parser for standard Markdown
805              
806             =head1 VERSION
807              
808             version 0.39
809              
810             =head1 DESCRIPTION
811              
812             This class parses blocks for the standard Markdown dialect (as defined by
813             Daring Fireball and mdtest).
814              
815             =head1 METHODS
816              
817             This class provides the following methods:
818              
819             =head2 Markdent::Parser::BlockParser->new( handler => $handler, span_parser => $span_parser )
820              
821             Creates a new block parser object. You must provide a span parser object.
822              
823             =head2 $block_parser->parse_document(\$markdown)
824              
825             This method takes a reference to a markdown string and parses it for
826             blocks. Each block which contains text (except preformatted text) will be
827             parsed for span-level markup using this object's C<span_parser>.
828              
829             =head1 ROLES
830              
831             This class does the L<Markdent::Role::BlockParser>,
832             L<Markdent::Role::AnyParser>, and L<Markdent::Role::DebugPrinter> roles.
833              
834             =head1 BUGS
835              
836             See L<Markdent> for bug reporting details.
837              
838             Bugs may be submitted at L<https://github.com/houseabsolute/Markdent/issues>.
839              
840             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
841              
842             =head1 SOURCE
843              
844             The source code repository for Markdent can be found at L<https://github.com/houseabsolute/Markdent>.
845              
846             =head1 AUTHOR
847              
848             Dave Rolsky <autarch@urth.org>
849              
850             =head1 COPYRIGHT AND LICENSE
851              
852             This software is copyright (c) 2021 by Dave Rolsky.
853              
854             This is free software; you can redistribute it and/or modify it under
855             the same terms as the Perl 5 programming language system itself.
856              
857             The full text of the license can be found in the
858             F<LICENSE> file included with this distribution.
859              
860             =cut