App-sdview
view release on metacpan or search on metacpan
lib/App/sdview/Parser/Markdown.pm view on Meta::CPAN
# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
# (C) Paul Evans, 2021-2023 -- leonerd@leonerd.org.uk
use v5.26;
use warnings;
use Object::Pad 0.807;
package App::sdview::Parser::Markdown 0.20;
class App::sdview::Parser::Markdown :strict(params);
apply App::sdview::Parser;
use File::Slurper 'read_text';
use String::Tagged::Markdown 0.05;
use constant format => "Markdown";
use constant sort_order => 20;
=head1 NAME
C<App::sdview::Parser::Markdown> - parse Markdown files for L<App::sdview>
=head1 SYNOPSIS
$ sdview README.md
$ sdview -f Markdown my-document
=head1 DESCRIPTION
This parser module adds to L<App::sdview> the ability to parse input text in
Markdown formatting.
It uses a custom in-built parser for the block-level parts of the formatting,
able to handle comments, verbatim blocks, headings in both C<#>-prefixed and
C<=>-underlined styles, bullet and numbered lists, and tables.
It uses L<String::Tagged::Markdown> to parse the inline-level formatting,
supporting bold, italic, strikethrough, and fixed-width styles, and links.
=cut
sub find_file ( $class, $name ) { return undef }
sub can_parse_file ( $class, $file )
{
return $file =~ m/\.(?:md|markdown)$/;
}
method parse_file ( $fh )
{
return $self->parse_string( read_text $fh );
}
field @_paragraphs;
sub _split_table_row ( $str )
{
$str =~ m/^\s*\|/ or return undef;
$str =~ m/\|\s*$/ or return undef;
my @cols = split m/\|/, $str, -1;
shift @cols; pop @cols;
s/^\s+//, s/\s+$// for @cols;
return \@cols;
}
method parse_string ( $str )
{
my $in_verb;
my @lines;
foreach ( split( m/\n/, $str ), "" ) {
my $line = $_; # So we have a copy, because foreach my ... will alias the readonly ""
if( $in_verb ) {
my $para = $_paragraphs[-1];
if( $line =~ m/^\`\`\`/ ) {
undef $in_verb;
next
}
length $para->text and
$para->text->append( "\n" );
$para->text->append( $line );
next;
}
if( $line =~ s/^\`\`\`// ) {
my $language = $line =~ s/^\s+|\s+$//gr;
push @_paragraphs, App::sdview::Para::Verbatim->new(
language => ( length $language ? $language : undef ),
text => String::Tagged->new,
);
lib/App/sdview/Parser/Markdown.pm view on Meta::CPAN
);
}
$list->push_item( App::sdview::Para::ListItem->new(
listtype => "number",
text => $self->_handle_spans( $raw )
) );
next;
}
elsif( @lines >= 2 and
my $cells = _split_table_row( $lines[0] ) and
my $aligns = _split_table_row( $lines[1] ) ) { # table
shift @lines;
my @colalign = map {
m/^(:?)-{3,}(:?)$/ or warn "Unexpected table heading separator text: $_\n";
( $1 and $2 ) ? "centre" :
( $2 ) ? "right" :
"left";
} @$aligns;
my @rows;
my $heading = !!1;
do {
shift @lines;
push @rows, [ map {
my $s = $cells->[$_];
App::sdview::Para::TableCell->new(
align => $colalign[$_],
heading => $heading,
text => $self->_handle_spans( $cells->[$_] ),
);
} 0 .. $#colalign ];
$heading = !!0;
} while( @lines and $cells = _split_table_row( $lines[0] ) );
push @_paragraphs, my $p = App::sdview::Para::Table->new( rows => \@rows );
}
else {
push @_paragraphs, App::sdview::Para::Plain->new(
text => $self->_handle_spans( join " ", @lines ),
);
}
@lines = ();
}
}
return @_paragraphs;
}
method _handle_spans ( $s )
{
return String::Tagged::Markdown->parse_markdown( $s )
->clone(
convert_tags => {
# bold, italic stay as they are
fixed => "monospace",
strike => "strikethrough",
link => sub ($t, $v) { return link => { uri => $v } },
},
only_tags => [qw( bold italic fixed strike link )],
);
}
=head1 AUTHOR
Paul Evans <leonerd@leonerd.org.uk>
=cut
0x55AA;
( run in 1.142 second using v1.01-cache-2.11-cpan-39bf76dae61 )