view release on metacpan or search on metacpan
0.14    2023-12-13
        [CHANGES]
         * Handle non-breaking spaces in Markdown and Pod
         * Updated for Object::Pad v0.807:
            + Use new `inherit` and `apply` keywords
0.13    2023-09-22
        [CHANGES]
         * Allow overriding of inline format styles in App::sdview::Style
           config file
         * Use format-agnostic tag names "bold", "italic", etc.. rather than
           POD-inspired "B", "I", etc..
         * Support Markdown's ~~strikethrough~~ format
         * Preserve the language name in Markdown code fences
         * Recognise and emit U<underline> formatting as a POD extension
0.12    2023-08-30
        [CHANGES]
         * Neater interaction between `margin` and `indent` style keys
         * User-overridable style formatting by providing a `~/.sdviewrc` file
lib/App/sdview/Output/Formatted.pm view on Meta::CPAN
   :$leader = undef,
   :$indent //= 0,
) {
   my %typestyle = App::sdview::Style->para_style( $para->type )->%*;
   $self->say() if $_nextblank;
   my $text = App::sdview::Style->convert_str( $para->text );
   $typestyle{$_} and $text->apply_tag( 0, -1, $_ => $typestyle{$_} )
      for qw( fg bg bold under italic monospace );
   $_nextblank = !!$typestyle{blank_after};
   my @lines = $text->split( qr/\n/ );
   @lines or @lines = ( String::Tagged->new ) if defined $leader;
   # If there's a background set, then space-pad every line to the same width
   # so it looks neater on the terminal
   #   https://rt.cpan.org/Ticket/Display.html?id=140536
   if( defined $typestyle{bg} ) {
lib/App/sdview/Output/Formatted.pm view on Meta::CPAN
         else {
            $part = $line;
            $line = "";
         }
         my $prefix = " "x$margin;;
         if( defined $leader ) {
            my %leaderstyle = App::sdview::Style->para_style( "leader" )->%*;
            $leaderstyle{$_} and $leader->apply_tag( 0, -1, $_ => $leaderstyle{$_} )
               for qw( fg bg bold under italic monospace );
            if( length($leader) + 1 <= $indent ) {
               # If the leader will fit on the same line with at least one space
               $prefix .= $leader . " "x($indent - length $leader);
            }
            else {
               # Spill the leader onto its own line
               $self->say( $prefix, $leader );
               $prefix .= " "x$indent if length $part;
lib/App/sdview/Output/Formatted.pm view on Meta::CPAN
      foreach my $colidx ( 0 .. $maxcol ) {
         my $cell = $row->[$colidx];
         my $text = App::sdview::Style->convert_str( $cell->text );
         my %cellstyle = %typestyle;
         %cellstyle = ( App::sdview::Style->para_style( "table-heading" )->%*, %cellstyle ) if $cell->heading;
         $cellstyle{$_} and $text->apply_tag( 0, -1, $_ => $cellstyle{$_} )
            for qw( fg bg bold under italic monospace );
         my $spare = $colwidths[$colidx] - length $text;
         my $leftpad = ( $cell->align eq "right"  ) ? " "x$spare :
                       ( $cell->align eq "centre" ) ? " "x($spare/2) :
                                                      "";
         my $rightpad = " "x($spare - length $leftpad);
         $out .= " " . $leftpad . $text . $rightpad . " ";
         $out .= "â";
      }
lib/App/sdview/Output/Man.pm view on Meta::CPAN
   my @fontstack;
   $s->iter_substr_nooverlap(
      sub ( $substr, %tags ) {
         $ret .= "\\fP", pop @fontstack
            while @fontstack and !$tags{ $fontstack[-1] };
         $tags{monospace} and (
            any { $_ eq "monospace" } @fontstack or
               $ret .= "\\f(CW", push @fontstack, "monospace" );
         $tags{bold} and (
            any { $_ eq "bold" } @fontstack or
               $ret .= "\\fB", push @fontstack, "bold" );
         $tags{italic} and (
            any { $_ eq "italic" } @fontstack or
               $ret .= "\\fI", push @fontstack, "italic" );
         my $man = $substr =~ s/([\\-])/\\$1/gr;
         $ret .= $man;
      }
   );
lib/App/sdview/Output/Markdown.pm view on Meta::CPAN
                                     ("-"x $n   );
      } @cells;
      $self->say( join "|", "", ( map { " $_ " } @aligns ), "" );
      undef $first;
   }
}
method _convert_str ( $s )
{
   return String::Tagged::Markdown->clone( $s,
      only_tags => [qw( bold italic monospace strikethrough file link )],
      convert_tags => {
         # bold, italic remain as they are
         monospace => "fixed",
         strikethrough => "strike",
         file => "italic", # There isn't a "filename" format in Markdown
         link => sub ($t, $v) { return link => $v->{uri} },
      }
   )->build_markdown;
}
=head1 AUTHOR
lib/App/sdview/Output/Pod.pm view on Meta::CPAN
            # TODO: This is even suckier than the bit in the parser
            if( $link->{uri} eq "https://metacpan.org/pod/$substr" ) {
               $pod = "L$open$substr$close";
            }
            else {
               $pod = "L$open$pod|$link->{uri}$close";
            }
         }
         $pod = "C$open$pod$close" if $tags{monospace};
         $pod = "B$open$pod$close" if $tags{bold};
         $pod = "I$open$pod$close" if $tags{italic};
         $pod = "F$open$pod$close" if $tags{file};
         $pod = "U$open$pod$close" if $tags{underline};
         $pod = "S$open$pod$close" if $tags{nobreak};
         $ret .= $pod;
      }
   );
lib/App/sdview/Parser/Man.pm view on Meta::CPAN
      }
      else {
         print STDERR "TODO: para->type = $type\n";
      }
   }
   return @_paragraphs;
}
my %FONTTAGS = (
   B  => { bold => 1 },
   I  => { italic => 1 },
   CW => { monospace => 1 },
);
sub _chunklist_to_taggedstring ( $chunks, :$linefeed = " " )
{
   my $ret = String::Tagged->new;
   foreach my $chunk ( $chunks->@* ) {
      my %tags;
lib/App/sdview/Parser/Markdown.pm view on Meta::CPAN
=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)$/;
}
lib/App/sdview/Parser/Markdown.pm view on Meta::CPAN
   }
   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;
lib/App/sdview/Parser/Pod.pm view on Meta::CPAN
role App::sdview::Parser::Pod::_TagHandler {
   ADJUST {
      $self->nix_X_codes( 1 );
      $self->accept_codes(qw( U ));
   }
   field %_curtags :reader;
   method reset_tags { %_curtags = (); }
   method start_B {        $_curtags{bold}++ }
   method end_B   { delete $_curtags{bold}   }
   method start_I {        $_curtags{italic}++ }
   method end_I   { delete $_curtags{italic}   }
   method start_U {        $_curtags{underline}++ }
   method end_U   { delete $_curtags{underline}   }
   method start_C {        $_curtags{monospace}++ }
   method end_C   { delete $_curtags{monospace}   }
   method start_F {        $_curtags{file}++ }
   method end_F   { delete $_curtags{file}   }
   method start_L ( $attrs )
lib/App/sdview/Style.pm view on Meta::CPAN
=head2 Config File
=for highlighter
Style information can be overridden by the user, supplying a
L<Config::Tiny>-style file at F<$HOME/.sdviewrc>. Formatting for each kind of
paragraph is provided in a section called C<Para $NAME>, and each individual
key gives formatting values.
   [Para head1]
   bold = 0|1
   italic = 0|1
   monospace = 0|1
   blank_after = 0|1
   under = NUM
   margin = NUM
   [Para head2]
   ...
Specifying the special value C<~> deletes the default value for that key
lib/App/sdview/Style.pm view on Meta::CPAN
=cut
sub _fixup_colour_keys ( $style )
{
   $style->{$_} and
      $style->{$_} = Convert::Color->new( $style->{$_} ) for qw( fg bg );
}
my %FORMATSTYLES = (
   bold          => { bold => 1 },
   italic        => { italic => 1 },
   monospace     => { monospace => 1, bg => "xterm:235" },
   underline     => { under => 1 },
   strikethrough => { strike => 1 },
   file => { italic => 1, under => 1 },
   link => { under => 1, fg => "xterm:rgb(3,3,5)" }, # light blue
);
_fixup_colour_keys $_ for values %FORMATSTYLES;
lib/App/sdview/Style.pm view on Meta::CPAN
            }
            else {
               $k => sub { $FORMATSTYLES{$k}->%* };
            }
         } keys %FORMATSTYLES ),
      },
   );
}
my %PARASTYLES = (
   head1    => { fg => "vga:yellow", bold => 1 },
   head2    => { fg => "vga:cyan",   bold => 1, margin => 2 },
   head3    => { fg => "vga:green",  bold => 1, margin => 4 },
   head4    => { fg => "xterm:217",  under => 1, margin => 5 },
   plain    => { margin => 6, blank_after => 1 },
   verbatim => { margin => 8, blank_after => 1, inherit => "monospace" },
   list     => { margin => 6 },
   item     => { blank_after => 1 },
   leader   => { bold => 1 },
   table    => { margin => 8 },
   "table-heading" => { bold => 1 },
);
_fixup_colour_keys $_ for values %PARASTYLES;
sub para_style ( $pkg, $type )
{
   $PARASTYLES{$type} or
      die "Unrecognised paragraph style for $type";
   my %style = $PARASTYLES{$type}->%*;
   %style = ( %style, $FORMATSTYLES{delete $style{inherit}}->%* ) if defined $style{inherit};
lib/App/sdview/Style.pm view on Meta::CPAN
   return \%style;
}
my %HIGHLIGHTSTYLES = (
   # Names stolen from tree-sitter's highlight theme
   attribute  => { fg => "vga:cyan", italic => 1 },
   character  => { fg => "vga:magenta" },
   comment    => { fg => "xterm:15", bg => "xterm:54", italic => 1 },
   decorator  => { fg => "xterm:140", italic => 1 },
   function   => { fg => "xterm:147", },
   keyword    => { fg => "vga:yellow", bold => 1 },
   module     => { fg => "vga:green", bold => 1 },
   number     => { fg => "vga:magenta" },
   operator   => { fg => "vga:yellow" },
   string     => { fg => "vga:magenta" },
   type       => { fg => "vga:green" },
   variable   => { fg => "vga:cyan" },
   'string.special' => { fg => "vga:red" },
   'function.builtin' => { fg => "xterm:147", bold => 1 },
);
$HIGHLIGHTSTYLES{$_} = { fallback => "keyword"  } for qw( include repeat conditional exception );
$HIGHLIGHTSTYLES{$_} = { fallback => "function" } for qw( method );
_fixup_colour_keys $_ for values %HIGHLIGHTSTYLES;
sub highlight_style ( $pkg, $key )
{
   my @nameparts = split m/\./, $key;
   while( @nameparts ) {
      my $style = $HIGHLIGHTSTYLES{ join ".", @nameparts } or
lib/App/sdview/Style.pm view on Meta::CPAN
      }
      return $style;
   }
   return undef;
}
my %VALID_STYLE_KEYS = map { $_ => 1 } qw(
   fg bg
   bold italic monospace blank_after
   under margin
);
sub _convert_val ( $stylekey, $val )
{
   return undef if !defined $val or $val eq "~";
   if( $stylekey =~ m/^(fg|bg)$/ ) {
      return Convert::Color->new( $val );
   }
   elsif( $stylekey =~ m/^(bold|italic|monospace|blank_after)$/ ) {
      return !!$val;
   }
   elsif( $stylekey =~ m/^(under|margin)$/ ) {
      return 0+$val;
   }
   else {
      return undef;
   }
}
t/01style.t view on Meta::CPAN
use Test2::V0;
use App::sdview::Style;
use Convert::Color;
# Default style
{
   is( App::sdview::Style->para_style( "head1" ),
      {
         bold => T(),
         fg   => Convert::Color->new( "vga:yellow" ),
      },
      'Default head1 paragraph style' );
   is( App::sdview::Style->inline_style( "monospace" ),
      {
         monospace => T(),
         bg        => Convert::Color->new( "xterm:235" ),
      },
      'Default monospace inline style' );
t/01style.t view on Meta::CPAN
      },
      'Default method highlight style falls back to keyword' );
}
# Load a custom config file
{
   App::sdview::Style->load_config( \*DATA );
   is( App::sdview::Style->para_style( "head1" ),
      {
         bold => T(),
         fg   => Convert::Color->new( "vga:red" ),
      },
      'Overridden head1 paragraph style' );
   is( App::sdview::Style->inline_style( "monospace" ),
      {
         monospace => T(),
      },
      'Overridden monospace inline style' );
t/10parser-pod.t view on Meta::CPAN
ok( App::sdview::Parser::Pod->can_parse_file( "Example.pod" ), 'Parser can handle .pod file' );
subtest "Basic" => sub {
   my @p = App::sdview::Parser::Pod->new->parse_string( <<"EOPOD" );
=head1 Heading
The heading paragraph here.
=head2 Content
The content with B<bold> and C<code> in it.
EOPOD
   is( scalar @p, 4, 'Received 4 paragraphs' );
   is( $p[0]->type, "head1", 'p[0] type' );
   is( $p[0]->text, "Heading", 'p[0] text' );
   is( $p[1]->type, "plain", 'p[1] type' );
   is( $p[1]->text, "The heading paragraph here.", 'p[1] text' );
   is( $p[2]->type, "head2", 'p[2] type' );
   is( $p[2]->text, "Content", 'p[2] text' );
   is( $p[3]->type, "plain", 'p[3] type' );
   is( $p[3]->text, "The content with bold and code in it.", 'p[3] text' );
   is( [ sort $p[3]->text->tagnames ], [qw( bold monospace )], 'p[3] tags' );
};
subtest "Formatting" => sub {
   my @p = App::sdview::Parser::Pod->new->parse_string( <<"EOPOD" );
=pod
B<bold> B<< bold >>
I<italic> I<< italic >>
C<code> C<< code->with->arrows >>
F<filename>
L<link|target://> L<Module::Here>
U<underline> U<< underline >>
EOPOD
   is( scalar @p, 6, 'Received 6 paragraphs' );
   is( $p[0]->text, "bold bold", 'bold text' );
   ok( $p[0]->text->get_tag_at( 0, "bold" ), 'bold tag' );
   is( $p[1]->text, "italic italic", 'italic text' );
   ok( $p[1]->text->get_tag_at( 0, "italic" ), 'italic tag' );
   is( $p[2]->text, "code code->with->arrows", 'code text' );
   ok( $p[2]->text->get_tag_at( 0, "monospace" ), 'code tag' );
   is( $p[3]->text, "filename", 'file text' );
   ok( $p[3]->text->get_tag_at( 0, "file" ), 'file tag' );
t/10parser-pod.t view on Meta::CPAN
subtest "Formatted headings" => sub {
   my @p = App::sdview::Parser::Pod->new->parse_string( <<"EOPOD" );
=head1 A B<Bold> Beginning
EOPOD
   is( scalar @p, 1, 'Received 1 paragraph' );
   is( $p[0]->type, "head1", 'p[0] type' );
   is( $p[0]->text, "A Bold Beginning", 'p[0] text' );
   is( [ sort $p[0]->text->tagnames ], [qw( bold )], 'p[0] tags' );
};
subtest "Non-breaking spaces" => sub {
   my @p = App::sdview::Parser::Pod->new->parse_string( <<"EOPOD" );
=pod
Some content with S<non-breaking spaces> in it.
EOPOD
   is( scalar @p, 1, 'Received 1 paragraph' );
t/10parser-pod.t view on Meta::CPAN
   is( $cells[0]->align, "left",   'col[0] align' );
   is( $cells[1]->text,  "Centre", 'col[1] text' );
   is( $cells[1]->align, "centre", 'col[1] align' );
   is( $cells[2]->text,  "Right",  'col[2] text' );
   is( $cells[2]->align, "right",  'col[2] align' );
   @rows = $p[2]->rows;
   my @col1 = map { $_->[1] } @rows;
   is( $col1[0]->text,  "123",                     'col1[0] text' );
   is( [ $col1[0]->text->tagnames ], [qw( bold )], 'col1[0] text tags' );
   ok( !$col1[0]->heading,                         'col1[0] heading' );
   is( $col1[1]->text,  "456",                       'col1[1] text' );
   is( [ $col1[1]->text->tagnames ], [qw( italic )], 'col1[1] text tags' );
   @rows = $p[3]->rows;
   is( $rows[0][0]->text, "A1", 'mediawiki cell A1' );
   ok( $rows[0][0]->heading,    'mediawiki cell A1 is heading' );
   is( $rows[0][1]->text, "A2", 'mediawiki cell A2' );
   ok( $rows[0][1]->heading,    'mediawiki cell A2 is heading' );
   is( $rows[1][0]->text, "B1", 'mediawiki cell B1' );
t/11parser-markdown.t view on Meta::CPAN
ok( App::sdview::Parser::Markdown->can_parse_file( "Example.md" ), 'Parser can handle .md file' );
subtest "Basic" => sub {
   my @p = App::sdview::Parser::Markdown->new->parse_string( <<"EOMARKDOWN" );
# Heading
The heading paragraph here.
## Content
The content with **bold** and `code` in it.
EOMARKDOWN
   is( scalar @p, 4, 'Received 4 paragraphs' );
   is( $p[0]->type, "head1", 'p[0] type' );
   is( $p[0]->text, "Heading", 'p[0] text' );
   is( $p[1]->type, "plain", 'p[1] type' );
   is( $p[1]->text, "The heading paragraph here.", 'p[1] text' );
   is( $p[2]->type, "head2", 'p[2] type' );
   is( $p[2]->text, "Content", 'p[2] text' );
   is( $p[3]->type, "plain", 'p[3] type' );
   is( $p[3]->text, "The content with bold and code in it.", 'p[3] text' );
   is( [ sort $p[3]->text->tagnames ], [qw( bold monospace )], 'p[3] tags' );
};
subtest "Alternate headings" => sub {
   my @p = App::sdview::Parser::Markdown->new->parse_string( <<"EOMARKDOWN" );
Heading
=======
The heading paragraph here.
Content
-------
The content with **bold** and `code` in it.
EOMARKDOWN
   is( scalar @p, 4, 'Received 4 paragraphs' );
   is( $p[0]->type, "head1", 'p[0] type' );
   is( $p[0]->text, "Heading", 'p[0] text' );
   is( $p[1]->type, "plain", 'p[1] type' );
   is( $p[1]->text, "The heading paragraph here.", 'p[1] text' );
   is( $p[2]->type, "head2", 'p[2] type' );
   is( $p[2]->text, "Content", 'p[2] text' );
   is( $p[3]->type, "plain", 'p[3] type' );
   is( $p[3]->text, "The content with bold and code in it.", 'p[3] text' );
};
subtest "Formatting" => sub {
   my @p = App::sdview::Parser::Markdown->new->parse_string( <<"EOMARKDOWN" );
**bold** __bold__
*italic* _italic_
`code` `code_with_unders`
[link](target://)
~~strikethrough~~
EOMARKDOWN
   is( scalar @p, 5, 'Received 5 paragraphs' );
   is( $p[0]->text, "bold bold", 'bold text' );
   ok( $p[0]->text->get_tag_at( 0, "bold" ), 'bold tag' );
   is( $p[1]->text, "italic italic", 'italic text' );
   ok( $p[1]->text->get_tag_at( 0, "italic" ), 'italic tag' );
   is( $p[2]->text, "code code_with_unders", 'code text' );
   ok( $p[2]->text->get_tag_at( 0, "monospace" ), 'code tag' );
   is( $p[3]->text, "link", 'link text' );
   is( $p[3]->text->get_tag_at( 0, "link" ), { uri => "target://" }, 'link tag' );
t/11parser-markdown.t view on Meta::CPAN
   is( $cells[0]->align, "left",   'col[0] align' );
   is( $cells[1]->text,  "Centre", 'col[1] text' );
   is( $cells[1]->align, "centre", 'col[1] align' );
   is( $cells[2]->text,  "Right",  'col[2] text' );
   is( $cells[2]->align, "right",  'col[2] align' );
   @rows = $p[2]->rows;
   my @col1 = map { $_->[1] } @rows;
   is( $col1[0]->text,  "123",                     'col1[0] text' );
   is( [ $col1[0]->text->tagnames ], [qw( bold )], 'col1[0] text tags' );
   is( $col1[1]->text,  "456",                       'col1[1] text' );
   is( [ $col1[1]->text->tagnames ], [qw( italic )], 'col1[1] text tags' );
};
done_testing;
t/12parser-man.t view on Meta::CPAN
isa_ok( $parser, [ "App::sdview::Parser::Man" ], '$parser' );
ok( App::sdview::Parser::Man->can_parse_file( "Example.3" ),  'Parser can handle .3 file' );
ok( App::sdview::Parser::Man->can_parse_file( "Example.3.gz" ), 'Parser can handle .3.gz file' );
subtest "Basic" => sub {
   my @p = App::sdview::Parser::Man->new->parse_string( <<"EOMAN" );
.SH Heading
The heading paragraph here.
.SS Content
The content with \\fBbold\\fP and \\f(CWcode\\fP in it.
EOMAN
   is( scalar @p, 4, 'Received 4 paragraphs' );
   is( $p[0]->type, "head1", 'p[0] type' );
   is( $p[0]->text, "Heading", 'p[0] text' );
   is( $p[1]->type, "plain", 'p[1] type' );
   is( $p[1]->text, "The heading paragraph here.", 'p[1] text' );
   is( $p[2]->type, "head2", 'p[2] type' );
   is( $p[2]->text, "Content", 'p[2] text' );
   is( $p[3]->type, "plain", 'p[3] type' );
   is( $p[3]->text, "The content with bold and code in it.", 'p[3] text' );
   is( [ sort $p[3]->text->tagnames ], [qw( bold monospace )], 'p[3] tags' );
};
subtest "Formatting" => sub {
   my @p = App::sdview::Parser::Man->new->parse_string( <<"EOMAN" );
.PP
.B bold
\\fBbold\\fP
.PP
.I italic
\\fIitalic\\fP
.PP
\\f(CWcode->with->arrows\\fP
EOMAN
   is( scalar @p, 3, 'Received 3 paragraphs' );
   is( $p[0]->text, "bold bold", 'bold text' );
   ok( $p[0]->text->get_tag_at( 0, "bold" ), 'bold tag' );
   is( $p[1]->text, "italic italic", 'italic text' );
   ok( $p[1]->text->get_tag_at( 0, "italic" ), 'italic tag' );
   is( $p[2]->text, "code->with->arrows", 'code text' );
   ok( $p[2]->text->get_tag_at( 0, "monospace" ), 'code tag' );
};
subtest "Verbatim trimming" => sub {
   my @p = App::sdview::Parser::Man->new->parse_string( <<"EOMAN" );
t/20output-pod.t view on Meta::CPAN
=head1 Head1
=head2 Head2
Contents here
EOPOD
dotest "Formatting", <<"EOPOD";
=pod
B<bold> B<< <bold> >>
I<italic>
C<code> C<< code->with->arrows >>
F<filename>
L<link|target://> L<Module::Here>
U<underline>
t/21output-markdown.t view on Meta::CPAN
dotest "Headings", <<"EOMARKDOWN";
# Heading
## Content
Contents here
EOMARKDOWN
dotest "Formatting", <<"EOMARKDOWN";
**bold**
*italic*
`code` `code_with_unders`
[link](target://)
~~strikethrough~~
EOMARKDOWN
t/22output-man.t view on Meta::CPAN
}
dotest "Headings", <<"EOMAN";
.SH Head1
.SS Head2
Contents here
EOMAN
dotest "Formatting", <<"EOMAN";
.PP
\\fBbold\\fP
.PP
\\fIitalic\\fP
.PP
\\f(CWcode\\->with\\->arrows\\fP
EOMAN
dotest "Verbatim", <<"EOMAN";
.SH EXAMPLE
.EX
use v5.14;
t/30output-plain.t view on Meta::CPAN
EOPOD
<<"EOF";
Head1
  Head2
      Contents here
EOF
dotest "Formatting", pod => <<"EOPOD",
=pod
B<bold> B<< <bold> >>
I<italic>
C<code> C<< code->with->arrows >>
L<link|target://> L<Module::Here>
EOPOD
<<"EOF";
      bold <bold>
      italic
      code code->with->arrows
      link Module::Here
EOF
dotest "Verbatim", pod => <<"EOPOD",
=head1 EXAMPLE