App-sdview

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

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



( run in 0.536 second using v1.01-cache-2.11-cpan-5dc5da66d9d )