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 34     34   293 use strict;
  34         79  
  34         1053  
4 34     34   220 use warnings;
  34         85  
  34         843  
5 34     34   183 use namespace::autoclean;
  34         80  
  34         186  
6              
7             our $VERSION = '0.38';
8              
9 34     34   21431 use Digest::SHA qw( sha1_hex );
  34         108895  
  34         3202  
10 34     34   882 use Encode qw( encode );
  34         10273  
  34         1693  
11 34     34   14635 use Markdent::Event::StartDocument;
  34         169  
  34         1865  
12 34     34   19931 use Markdent::Event::EndDocument;
  34         138  
  34         1327  
13 34     34   17248 use Markdent::Event::StartBlockquote;
  34         133  
  34         1347  
14 34     34   16816 use Markdent::Event::EndBlockquote;
  34         149  
  34         1594  
15 34     34   17015 use Markdent::Event::StartHeader;
  34         151  
  34         1409  
16 34     34   16534 use Markdent::Event::EndHeader;
  34         152  
  34         1425  
17 34     34   19614 use Markdent::Event::StartListItem;
  34         153  
  34         1432  
18 34     34   19238 use Markdent::Event::EndListItem;
  34         134  
  34         1336  
19 34     34   17450 use Markdent::Event::StartOrderedList;
  34         137  
  34         1331  
20 34     34   17280 use Markdent::Event::EndOrderedList;
  34         144  
  34         1361  
21 34     34   17298 use Markdent::Event::StartParagraph;
  34         144  
  34         1278  
22 34     34   17323 use Markdent::Event::EndParagraph;
  34         166  
  34         1293  
23 34     34   17470 use Markdent::Event::StartUnorderedList;
  34         155  
  34         1420  
24 34     34   16971 use Markdent::Event::EndUnorderedList;
  34         138  
  34         1307  
25 34     34   16946 use Markdent::Event::HorizontalRule;
  34         151  
  34         1342  
26 34     34   17612 use Markdent::Event::HTMLBlock;
  34         151  
  34         1431  
27 34     34   20110 use Markdent::Event::HTMLCommentBlock;
  34         162  
  34         1483  
28 34     34   19686 use Markdent::Event::Preformatted;
  34         169  
  34         1595  
29 34     34   18199 use Markdent::Regexes qw( :block $HTMLComment );
  34         143  
  34         6264  
30 34     34   301 use Markdent::Types;
  34         87  
  34         327  
31              
32 34     34   863675 use Moose;
  34         164  
  34         403  
33 34     34   245716 use MooseX::SemiAffordanceAccessor;
  34         92  
  34         322  
34 34     34   125333 use MooseX::StrictConstructor;
  34         98  
  34         285  
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 175     175 1 369 my $self = shift;
76 175         333 my $text = shift;
77              
78 175         7321 $self->_treat_list_item_as_line();
79              
80 175         739 $self->_hash_html_blocks($text);
81              
82 175         5444 $self->_span_parser()->extract_link_ids($text);
83              
84 175         690 $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 175     175   347 my $self = shift;
99 175         306 my $text = shift;
100              
101 175         337 ${$text} =~ s{
  175         3370  
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     1794 }
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 175         9831 # an ending delimiter.
  175         2089  
114             ${$text} =~ s{
115             ( $BlockStart )
116 0   0     0 (<hr.*\ */?>)
117             }
118 175         3992 { ( $1 || q{} ) . $self->_hash_and_save_html($2) }egxm;
119              
120             return;
121             }
122             }
123 14     14   33  
124 14         44 sub _hash_and_save_html {
125             my $self = shift;
126 14         83 my $html = shift;
127              
128 14         2570 my $sha1 = lc sha1_hex( encode( 'UTF-8', $html ) );
129              
130 14         7174 $self->_save_html_block( $sha1 => $html );
131              
132             return 'html:' . $sha1 . "\n";
133             }
134 905     905   1600  
135 905         1298 sub _parse_text {
136             my $self = shift;
137 905         1352 my $text = shift;
138              
139 905         1539 my $last_pos;
140 2253 50 33     64249 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 2253 100       3734 }
  2253         8984  
147 903         2996  
148             if ( ${$text} =~ / \G \p{Space}* \z /xgc ) {
149             last;
150 1350   100     2269 }
151 1350 50 33     3475  
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 1350         4385 }
162              
163 1350         5095 my @look_for = $self->_possible_block_matches();
164              
165 1350         3104 $self->_debug_look_for(@look_for);
166 11737         148661  
167             for my $block (@look_for) {
168 11737 100       29431 my $meth = '_match_' . $block;
169              
170             $self->$meth($text)
171             and next PARSE;
172 620   50     3810 }
173              
174             $last_pos = pos ${$text} || 0;
175             }
176             }
177 1350     1350   2120  
178             sub _possible_block_matches {
179 1350         1998 my $self = shift;
180              
181 1350 100       39037 my @look_for;
182              
183             push @look_for, qw( hashed_html horizontal_rule )
184 1350         3538 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 1350 100       35784 );
194              
195             push @look_for, 'list_item'
196 1350         2729 if $self->_list_level();
197              
198 1350         5019 push @look_for, 'paragraph';
199              
200             return @look_for;
201             }
202              
203 1253     1253   1991 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
204 1253         1916 sub _match_hashed_html {
205             my $self = shift;
206 1253 100       1957 my $text = shift;
  1253         10986  
207              
208             return unless ${$text} =~ / \G
209             $BlockStart
210             ^
211             (
212             html:([0-9a-f]{40})
213             \n
214             )
215             $BlockEnd
216 14         578 /xmgc;
217              
218 14 50       71 my $html = $self->_get_html_block($2);
219              
220 14 50       412 return unless defined $html;
221              
222             $self->_debug_parse_result(
223             $1,
224             'hashed html',
225 14         66 ) if $self->debug();
226              
227             $self->_send_event(
228             HTMLBlock => html => $html,
229 14         1398 );
230              
231             return 1;
232             }
233 1323     1323   2187  
234 1323         2209 sub _match_html_comment {
235             my $self = shift;
236 1323 100       1895 my $text = shift;
  1323         9716  
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       92 my $comment = $1;
248              
249             $self->_debug_parse_result(
250             $comment,
251             'html comment block',
252 3         16 ) if $self->debug();
253              
254 3         10 $self->_detab_text( \$comment );
255              
256 3         271 $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 1320     1320   2187  
275 1320         2041 sub _match_atx_header {
276             my $self = shift;
277 1320 100       1818 my $text = shift;
  1320         9563  
278              
279             return unless ${$text} =~ / \G
280             (?:$EmptyLines)?
281             ($AtxHeader)
282 43         450 /xmgc;
283 43         159  
284             my $level = length $2;
285 43 50       1257 my $header_text = $3 . "\n";
286              
287             $self->_debug_parse_result(
288             $1,
289             'atx header',
290             [ level => $level ],
291 43         389 ) if $self->debug();
292              
293 43         1041 $header_text =~ s/^$HorizontalWS*//;
294              
295 43         239 $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 1277     1277   1996  
311 1277         1890 sub _match_two_line_header {
312             my $self = shift;
313 1277 100       1777 my $text = shift;
  1277         9577  
314              
315             return unless ${$text} =~ / \G
316             (?:$EmptyLines)?
317             ($TwoLineHeader)
318 19 100       1539 /xmgc;
319              
320 19 50       667 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         105 $self->_header( $level, $2 );
329              
330             return 1;
331             }
332 62     62   135  
333 62         104 sub _header {
334 62         137 my $self = shift;
335             my $level = shift;
336 62         238 my $text = shift;
337              
338 62         1904 $self->_send_event( StartHeader => level => $level );
339              
340 62         346 $self->_span_parser()->parse_block($text);
341              
342 62         1653 $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 1239     1239   1986  
361 1239         1793 sub _match_horizontal_rule {
362             my $self = shift;
363 1239 100       1848 my $text = shift;
  1239         12364  
364              
365             return unless ${$text} =~ / \G
366             (?:$EmptyLines)?
367             $HorizontalRule
368 6 50       763 /xmgc;
369              
370             $self->_debug_parse_result(
371             $1,
372             'horizontal rule',
373 6         26 ) if $self->debug();
374              
375 6         712 $self->_send_event('HorizontalRule');
376              
377             return 1;
378             }
379 1258     1258   1976  
380 1258         1841 sub _match_blockquote {
381             my $self = shift;
382 1258 100       1755 my $text = shift;
  1258         13755  
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         893 /xmgc;
412              
413 21 50       664 my $bq = $1;
414              
415             $self->_debug_parse_result(
416             $bq,
417             'blockquote',
418 21         97 ) if $self->debug();
419              
420 21         686 $self->_send_event('StartBlockquote');
421              
422             $bq =~ s/^>$HorizontalWS?//gxm;
423              
424 21         895 # Even if the blockquote is inside a list, we want to look for paragraphs,
425 21         697 # 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         172 # 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         106 $self->_split_chunks_on_regex( $bq, qr/^>$HorizontalWS*\S/xm ) ) {
434              
435             $self->_parse_text( \$chunk );
436 21         784 }
437              
438 21         84 $self->_set_list_level($list_level);
439              
440 21         643 $self->_send_event('EndBlockquote');
441              
442             return 1;
443             }
444             ## use critic
445 21     21   251  
446 21         38 sub _split_chunks_on_regex {
447 21         44 my $self = shift;
448             my $text = shift;
449 21         40 my $regex = shift;
450              
451 21         37 my @chunks;
452             my @chunk;
453 21         115 my $in_regex = 0;
454 57         84  
455             for my $line ( split /\n/, $text ) {
456 57 100 66     424 my $new_chunk;
    100 66        
457 3         6  
458 3         9 if ( $in_regex && $line !~ $regex ) {
459             $in_regex = 0;
460             $new_chunk = 1;
461 5         14 }
462 5         12 elsif ( $line =~ $regex && !$in_regex ) {
463             $in_regex = 1;
464             $new_chunk = 1;
465 57 100       131 }
466 8 50       26  
  10         42  
467             if ($new_chunk) {
468 8         22 push @chunks, join q{}, map { $_ . "\n" } @chunk
469             if @chunk;
470             @chunk = ();
471 57         169 }
472              
473             push @chunk, $line;
474 21 50       85 }
  47         153  
475              
476             push @chunks, join q{}, map { $_ . "\n" } @chunk
477 21         74 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 1237     1237   1966 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
495 1237         1881 sub _match_preformatted {
496             my $self = shift;
497 1237 100       1751 my $text = shift;
  1237         13405  
498              
499             return unless ${$text} =~ / \G
500             $BlockStart
501             (
502             (?:
503             $PreLine
504             (?:$EmptyLine)*
505             )*
506             $PreLine
507             )
508 142         1691 /xmgc;
509              
510 142 50       4458 my $pre = $1;
511              
512             $self->_debug_parse_result(
513             $pre,
514             'preformatted',
515 142         1240 ) if $self->debug();
516              
517 142         600 $pre =~ s/^(?:\p{SpaceSeparator}{4}|\t)//gm;
518              
519 142         430 $self->_detab_text( \$pre );
520              
521 142         12859 $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 2062     2062   2928  
538             sub _list_re {
539 2062         2768 my $self = shift;
540              
541 2062 100       61266 my $block_start;
542 98         543  
543             if ( $self->_list_level() ) {
544             $block_start = qr/(?: (?<= \n ) | $EmptyLines )/xm;
545 1964         9551 }
546             else {
547             $block_start = qr/ $BlockStart /xm;
548 2062         16372 }
549              
550             my $list = qr/ $block_start
551             (
552             $Bullet
553             (?: .* \n )+?
554             )
555 2062         30800 /xm;
556              
557             return $list;
558             }
559              
560 1095     1095   1678 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
561 1095         1623 sub _match_list {
562             my $self = shift;
563 1095         2216 my $text = shift;
564              
565 1095 100       1712 my $list_re = $self->_list_re();
  1095         16579  
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         17319 /xmgc;
588 39         90  
589             my $list = $1;
590 39 100       143 my $bullet = $2;
591              
592 39 50       1121 my $type = $bullet =~ /\d/ ? 'OrderedList' : 'UnorderedList';
593              
594             $self->_debug_parse_result(
595             $list,
596             $type,
597 39         196 ) if $self->debug();
598              
599 39         1155 $self->_send_event( 'Start' . $type );
600              
601 39         115 $self->_inc_list_level();
602              
603 39         154 my @items = $self->_split_list_items($list);
604              
605 39         1328 $self->_handle_list_items( $type, @items );
606              
607 39         169 $self->_dec_list_level();
608              
609 39         1070 $self->_send_event( 'End' . $type );
610              
611             return 1;
612             }
613             ## use critic
614 39     39   61  
615 39         95 sub _split_list_items {
616             my $self = shift;
617 39         68 my $list = shift;
618              
619             my @items;
620 39         177 my @chunk;
621              
622 113 100 100     843 for my $line ( split /\n/, $list ) {
623             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
624 36         106 if ( $line =~ /^$Bullet/ && @chunk ) {
  65         207  
625             ## use critic
626 36         81 push @items, join q{}, map { $_ . "\n" } @chunk;
627              
628             @chunk = ();
629 113         1935 }
630              
631             push @chunk, $line;
632 39 50       134 }
  48         146  
633              
634             push @items, join q{}, map { $_ . "\n" } @chunk
635 39         119 if @chunk;
636              
637             return @items;
638             }
639 39     39   66  
640 39         63 sub _handle_list_items {
641 39         85 my $self = shift;
642             my $type = shift;
643 39         61 my @items = @_;
644 39         84  
645 75         562 my $ordinal_list_num = 1;
646             for my $item (@items) {
647             $item =~ s/^$Bullet//;
648 75 100       1774  
649             ## no critic (RegularExpressions::ProhibitCaptureWithoutTest)
650             my $bullet
651             = $type eq 'OrderedList' ? ( $ordinal_list_num++ ) . q{.} : $1;
652 75         269 ## 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         2027 # 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       2116 $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       495 # item is a paragraph) also is treated as a paragraph, not just a list
    100          
666 39 100 100     416 # item.
667             if ( $item eq $items[-1] ) {
668             if ( @items > 1
669 5 50       126 && $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         185 ) if $self->debug();
674              
675             $self->_treat_list_item_as_paragraph();
676 34         1919 }
677             else {
678             $self->_treat_list_item_as_line();
679             }
680 6 50       155 }
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         225 ) if $self->debug();
685              
686             $self->_treat_list_item_as_paragraph();
687 30         1814 }
688             else {
689             $self->_treat_list_item_as_line();
690 75         330 }
691              
692 75         232 $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   150 ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
704 78         110 sub _match_list_item {
705             my $self = shift;
706 78 100       107 my $text = shift;
  78         835  
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       4459 /xmgc;
729              
730             $self->_debug_parse_result(
731             $1,
732             'list_item',
733 68 100       1890 ) if $self->debug();
734              
735             $self->_send_event('StartParagraph')
736 68         1698 if $self->_list_item_is_paragraph();
737              
738 68 100       1793 $self->_span_parser()->parse_block($1);
739              
740             $self->_send_event('EndParagraph')
741 68         414 if $self->_list_item_is_paragraph();
742              
743             return 1;
744             }
745 967     967   1658  
746 967         1455 sub _match_paragraph {
747             my $self = shift;
748 967         2030 my $text = shift;
749              
750             my $list_re = $self->_list_re();
751 967 100       1610  
  967         18817  
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 347 50       47178 /xmgc;
774              
775             $self->_debug_parse_result(
776             $1,
777             'paragraph',
778 347         1336 ) if $self->debug();
779              
780 345         10859 $self->_send_event('StartParagraph');
781              
782 345         1523 $self->_span_parser()->parse_block($1);
783              
784 345         9204 $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.38
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) 2020 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