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   270 use strict;
  35         80  
  35         1060  
4 35     35   181 use warnings;
  35         72  
  35         830  
5 35     35   189 use namespace::autoclean;
  35         91  
  35         213  
6              
7             our $VERSION = '0.40';
8              
9 35     35   23521 use Digest::SHA qw( sha1_hex );
  35         118336  
  35         3673  
10 35     35   979 use Encode qw( encode );
  35         10730  
  35         1903  
11 35     35   17441 use Markdent::Event::StartDocument;
  35         181  
  35         2162  
12 35     35   22715 use Markdent::Event::EndDocument;
  35         141  
  35         1486  
13 35     35   20518 use Markdent::Event::StartBlockquote;
  35         136  
  35         1617  
14 35     35   20748 use Markdent::Event::EndBlockquote;
  35         197  
  35         1464  
15 35     35   20441 use Markdent::Event::StartHeader;
  35         149  
  35         1650  
16 35     35   20131 use Markdent::Event::EndHeader;
  35         176  
  35         1634  
17 35     35   21736 use Markdent::Event::StartListItem;
  35         232  
  35         1640  
18 35     35   21727 use Markdent::Event::EndListItem;
  35         139  
  35         1532  
19 35     35   21210 use Markdent::Event::StartOrderedList;
  35         145  
  35         1519  
20 35     35   20943 use Markdent::Event::EndOrderedList;
  35         152  
  35         1637  
21 35     35   20613 use Markdent::Event::StartParagraph;
  35         151  
  35         1489  
22 35     35   20686 use Markdent::Event::EndParagraph;
  35         149  
  35         1466  
23 35     35   20594 use Markdent::Event::StartUnorderedList;
  35         146  
  35         1588  
24 35     35   20703 use Markdent::Event::EndUnorderedList;
  35         148  
  35         1580  
25 35     35   20343 use Markdent::Event::HorizontalRule;
  35         145  
  35         2325  
26 35     35   19894 use Markdent::Event::HTMLBlock;
  35         150  
  35         1550  
27 35     35   21607 use Markdent::Event::HTMLCommentBlock;
  35         177  
  35         1634  
28 35     35   21491 use Markdent::Event::Preformatted;
  35         479  
  35         1719  
29 35     35   20258 use Markdent::Regexes qw( :block $HTMLComment );
  35         121  
  35         6918  
30 35     35   338 use Markdent::Types;
  35         89  
  35         335  
31              
32 35     35   911392 use Moose;
  35         132  
  35         423  
33 35     35   259598 use MooseX::SemiAffordanceAccessor;
  35         95  
  35         352  
34 35     35   134863 use MooseX::StrictConstructor;
  35         104  
  35         322  
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 180     180 1 402 my $self = shift;
76 180         354 my $text = shift;
77              
78 180         7886 $self->_treat_list_item_as_line;
79              
80 180         784 $self->_hash_html_blocks($text);
81              
82 180         5905 $self->_span_parser->extract_link_ids($text);
83              
84 180         732 $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 180     180   407 my $self = shift;
99 180         330 my $text = shift;
100              
101 180         335 ${$text} =~ s{
  180         3723  
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     1929 }
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 180         10170 # an ending delimiter.
  180         1676  
114             ${$text} =~ s{
115             ( $BlockStart )
116 0   0     0 (<hr.*\ */?>)
117             }
118 180         4182 { ( $1 || q{} ) . $self->_hash_and_save_html($2) }egxm;
119              
120             return;
121             }
122             }
123 14     14   38  
124 14         45 sub _hash_and_save_html {
125             my $self = shift;
126 14         110 my $html = shift;
127              
128 14         2977 my $sha1 = lc sha1_hex( encode( 'UTF-8', $html ) );
129              
130 14         7401 $self->_save_html_block( $sha1 => $html );
131              
132             return 'html:' . $sha1 . "\n";
133             }
134 910     910   1584  
135 910         1328 sub _parse_text {
136             my $self = shift;
137 910         1382 my $text = shift;
138              
139 910         1380 my $last_pos;
140 2263 50 33     66723 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 2263 100       3775 }
  2263         9333  
147 908         2981  
148             if ( ${$text} =~ / \G \p{Space}* \z /xgc ) {
149             last;
150 1355   100     2423 }
151 1355 50 33     3584  
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 1355         4219 }
162              
163 1355         5269 my @look_for = $self->_possible_block_matches;
164              
165 1355         3244 $self->_debug_look_for(@look_for);
166 11783         158381  
167             for my $block (@look_for) {
168 11783 100       30425 my $meth = '_match_' . $block;
169              
170             $self->$meth($text)
171             and next PARSE;
172 620   50     3839 }
173              
174             $last_pos = pos ${$text} || 0;
175             }
176             }
177 1355     1355   2201  
178             sub _possible_block_matches {
179 1355         2047 my $self = shift;
180              
181 1355 100       40374 my @look_for;
182              
183             push @look_for, qw( hashed_html horizontal_rule )
184 1355         3478 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 1355 100       37495 );
194              
195             push @look_for, 'list_item'
196 1355         2793 if $self->_list_level;
197              
198 1355         5039 push @look_for, 'paragraph';
199              
200             return @look_for;
201             }
202              
203 1258     1258   2132 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
204 1258         1905 sub _match_hashed_html {
205             my $self = shift;
206 1258 100       1863 my $text = shift;
  1258         11340  
207              
208             return unless ${$text} =~ / \G
209             $BlockStart
210             ^
211             (
212             html:([0-9a-f]{40})
213             \n
214             )
215             $BlockEnd
216 14         597 /xmgc;
217              
218 14 50       51 my $html = $self->_get_html_block($2);
219              
220 14 50       418 return unless defined $html;
221              
222             $self->_debug_parse_result(
223             $1,
224             'hashed html',
225 14         78 ) if $self->debug;
226              
227             $self->_send_event(
228             HTMLBlock => html => $html,
229 14         1607 );
230              
231             return 1;
232             }
233 1328     1328   2209  
234 1328         2029 sub _match_html_comment {
235             my $self = shift;
236 1328 100       1929 my $text = shift;
  1328         10102  
237              
238             return unless ${$text} =~ / \G
239             $EmptyLine*?
240             ^
241             \p{SpaceSeparator}{0,3}
242             $HTMLComment
243             $HorizontalWS*
244             \n
245 3         11 /xmgc;
246              
247 3 50       89 my $comment = $1;
248              
249             $self->_debug_parse_result(
250             $comment,
251             'html comment block',
252 3         18 ) if $self->debug;
253              
254 3         14 $self->_detab_text( \$comment );
255              
256 3         265 $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 1325     1325   2102  
275 1325         1921 sub _match_atx_header {
276             my $self = shift;
277 1325 100       1873 my $text = shift;
  1325         10157  
278              
279             return unless ${$text} =~ / \G
280             (?:$EmptyLines)?
281             ($AtxHeader)
282 43         453 /xmgc;
283 43         164  
284             my $level = length $2;
285 43 50       1261 my $header_text = $3 . "\n";
286              
287             $self->_debug_parse_result(
288             $1,
289             'atx header',
290             [ level => $level ],
291 43         435 ) if $self->debug;
292              
293 43         1092 $header_text =~ s/^$HorizontalWS*//;
294              
295 43         244 $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 1282     1282   2095  
311 1282         1884 sub _match_two_line_header {
312             my $self = shift;
313 1282 100       1869 my $text = shift;
  1282         10238  
314              
315             return unless ${$text} =~ / \G
316             (?:$EmptyLines)?
317             ($TwoLineHeader)
318 19 100       1544 /xmgc;
319              
320 19 50       589 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         76 ) if $self->debug;
327              
328 19         113 $self->_header( $level, $2 );
329              
330             return 1;
331             }
332 62     62   450  
333 62         129 sub _header {
334 62         125 my $self = shift;
335             my $level = shift;
336 62         240 my $text = shift;
337              
338 62         1961 $self->_send_event( StartHeader => level => $level );
339              
340 62         305 $self->_span_parser->parse_block($text);
341              
342 62         1565 $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 1244     1244   2092  
361 1244         1919 sub _match_horizontal_rule {
362             my $self = shift;
363 1244 100       1708 my $text = shift;
  1244         12429  
364              
365             return unless ${$text} =~ / \G
366             (?:$EmptyLines)?
367             $HorizontalRule
368 6 50       641 /xmgc;
369              
370             $self->_debug_parse_result(
371             $1,
372             'horizontal rule',
373 6         25 ) if $self->debug;
374              
375 6         725 $self->_send_event('HorizontalRule');
376              
377             return 1;
378             }
379 1263     1263   2205  
380 1263         1982 sub _match_blockquote {
381             my $self = shift;
382 1263 100       1925 my $text = shift;
  1263         14411  
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         908 /xmgc;
412              
413 21 50       636 my $bq = $1;
414              
415             $self->_debug_parse_result(
416             $bq,
417             'blockquote',
418 21         82 ) if $self->debug;
419              
420 21         655 $self->_send_event('StartBlockquote');
421              
422             $bq =~ s/^>$HorizontalWS?//gxm;
423              
424 21         849 # Even if the blockquote is inside a list, we want to look for paragraphs,
425 21         703 # 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         153 # 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         101 $self->_split_chunks_on_regex( $bq, qr/^>$HorizontalWS*\S/xm ) ) {
434              
435             $self->_parse_text( \$chunk );
436 21         752 }
437              
438 21         74 $self->_set_list_level($list_level);
439              
440 21         638 $self->_send_event('EndBlockquote');
441              
442             return 1;
443             }
444             ## use critic
445 21     21   277  
446 21         44 sub _split_chunks_on_regex {
447 21         35 my $self = shift;
448             my $text = shift;
449 21         38 my $regex = shift;
450              
451 21         38 my @chunks;
452             my @chunk;
453 21         104 my $in_regex = 0;
454 57         84  
455             for my $line ( split /\n/, $text ) {
456 57 100 66     339 my $new_chunk;
    100 66        
457 3         7  
458 3         6 if ( $in_regex && $line !~ $regex ) {
459             $in_regex = 0;
460             $new_chunk = 1;
461 5         12 }
462 5         11 elsif ( $line =~ $regex && !$in_regex ) {
463             $in_regex = 1;
464             $new_chunk = 1;
465 57 100       113 }
466 8 50       28  
  10         42  
467             if ($new_chunk) {
468 8         17 push @chunks, join q{}, map { $_ . "\n" } @chunk
469             if @chunk;
470             @chunk = ();
471 57         135 }
472              
473             push @chunk, $line;
474 21 50       75 }
  47         152  
475              
476             push @chunks, join q{}, map { $_ . "\n" } @chunk
477 21         72 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 1242     1242   2072 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
495 1242         1922 sub _match_preformatted {
496             my $self = shift;
497 1242 100       1910 my $text = shift;
  1242         14870  
498              
499             return unless ${$text} =~ / \G
500             $BlockStart
501             (
502             (?:
503             $PreLine
504             (?:$EmptyLine)*
505             )*
506             $PreLine
507             )
508 142         1815 /xmgc;
509              
510 142 50       4657 my $pre = $1;
511              
512             $self->_debug_parse_result(
513             $pre,
514             'preformatted',
515 142         1339 ) if $self->debug;
516              
517 142         623 $pre =~ s/^(?:\p{SpaceSeparator}{4}|\t)//gm;
518              
519 142         480 $self->_detab_text( \$pre );
520              
521 142         13309 $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 2072     2072   3095  
538             sub _list_re {
539 2072         3184 my $self = shift;
540              
541 2072 100       63298 my $block_start;
542 98         612  
543             if ( $self->_list_level ) {
544             $block_start = qr/(?: (?<= \n ) | $EmptyLines )/xm;
545 1974         10003 }
546             else {
547             $block_start = qr/ $BlockStart /xm;
548 2072         17712 }
549              
550             my $list = qr/ $block_start
551             (
552             $Bullet
553             (?: .* \n )+?
554             )
555 2072         34136 /xm;
556              
557             return $list;
558             }
559              
560 1100     1100   1818 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
561 1100         1580 sub _match_list {
562             my $self = shift;
563 1100         2567 my $text = shift;
564              
565 1100 100       1830 my $list_re = $self->_list_re;
  1100         17395  
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         20990 /xmgc;
588 39         98  
589             my $list = $1;
590 39 100       187 my $bullet = $2;
591              
592 39 50       1302 my $type = $bullet =~ /\d/ ? 'OrderedList' : 'UnorderedList';
593              
594             $self->_debug_parse_result(
595             $list,
596             $type,
597 39         219 ) if $self->debug;
598              
599 39         1446 $self->_send_event( 'Start' . $type );
600              
601 39         141 $self->_inc_list_level;
602              
603 39         165 my @items = $self->_split_list_items($list);
604              
605 39         1515 $self->_handle_list_items( $type, @items );
606              
607 39         182 $self->_dec_list_level;
608              
609 39         1235 $self->_send_event( 'End' . $type );
610              
611             return 1;
612             }
613             ## use critic
614 39     39   79  
615 39         75 sub _split_list_items {
616             my $self = shift;
617 39         87 my $list = shift;
618              
619             my @items;
620 39         251 my @chunk;
621              
622 113 100 100     900 for my $line ( split /\n/, $list ) {
623             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
624 36         96 if ( $line =~ /^$Bullet/ && @chunk ) {
  65         228  
625             ## use critic
626 36         90 push @items, join q{}, map { $_ . "\n" } @chunk;
627              
628             @chunk = ();
629 113         1930 }
630              
631             push @chunk, $line;
632 39 50       149 }
  48         167  
633              
634             push @items, join q{}, map { $_ . "\n" } @chunk
635 39         130 if @chunk;
636              
637             return @items;
638             }
639 39     39   69  
640 39         76 sub _handle_list_items {
641 39         138 my $self = shift;
642             my $type = shift;
643 39         84 my @items = @_;
644 39         83  
645 75         702 my $ordinal_list_num = 1;
646             for my $item (@items) {
647             $item =~ s/^$Bullet//;
648 75 100       1850  
649             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
650             my $bullet
651             = $type eq 'OrderedList' ? ( $ordinal_list_num++ ) . q{.} : $1;
652 75         294 ## 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         2351 # 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       2351 $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       538 # item is a paragraph) also is treated as a paragraph, not just a list
    100          
666 39 100 100     441 # item.
667             if ( $item eq $items[-1] ) {
668             if ( @items > 1
669 5 50       143 && $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         208 ) if $self->debug;
674              
675             $self->_treat_list_item_as_paragraph;
676 34         2126 }
677             else {
678             $self->_treat_list_item_as_line;
679             }
680 6 50       170 }
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         254 ) if $self->debug;
685              
686             $self->_treat_list_item_as_paragraph;
687 30         1975 }
688             else {
689             $self->_treat_list_item_as_line;
690 75         302 }
691              
692 75         275 $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   152 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
704 78         150 sub _match_list_item {
705             my $self = shift;
706 78 100       117 my $text = shift;
  78         958  
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       4688 /xmgc;
729              
730             $self->_debug_parse_result(
731             $1,
732             'list_item',
733 68 100       2159 ) if $self->debug;
734              
735             $self->_send_event('StartParagraph')
736 68         1906 if $self->_list_item_is_paragraph;
737              
738 68 100       2368 $self->_span_parser->parse_block($1);
739              
740             $self->_send_event('EndParagraph')
741 68         497 if $self->_list_item_is_paragraph;
742              
743             return 1;
744             }
745 972     972   1727  
746 972         1649 sub _match_paragraph {
747             my $self = shift;
748 972         2267 my $text = shift;
749              
750             my $list_re = $self->_list_re;
751 972 100       1675  
  972         19832  
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 352 50       50669 /xmgc;
774              
775             $self->_debug_parse_result(
776             $1,
777             'paragraph',
778 352         1604 ) if $self->debug;
779              
780 350         11117 $self->_send_event('StartParagraph');
781              
782 350         1712 $self->_span_parser->parse_block($1);
783              
784 350         10221 $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.40
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 blocks.
826             Each block which contains text (except preformatted text) will be parsed for
827             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