App-sdview

 view release on metacpan or  search on metacpan

lib/App/sdview/Output/Formatted.pm  view on Meta::CPAN

=head1 NAME

C<App::sdview::Output::Formatted> - base class for generating formatted output from L<App::sdview>

=head1 DESCRIPTION

This module is the base class used by  both L<App::sdview::Output::Plain> and
L<App::sdview::Output::Terminal>. It shouldn't be used directly.

=cut

field $_TERMWIDTH;
field $_nextblank;

method output ( @paragraphs )
{
   $self->setup_output();

   $_TERMWIDTH = $self->width;

   foreach my $para ( @paragraphs ) {
      my $code = $self->can( "output_" . ( $para->type =~ s/-/_/gr ) )
         or die "TODO: Unhandled paragraph type " . $para->type;

      $self->$code( $para );
   }
}

# Most paragraphs are handled in a uniform way
*output_head1 = \&_output_para;
*output_head2 = \&_output_para;
*output_head3 = \&_output_para;
*output_head4 = \&_output_para;

*output_plain = \&_output_para;

*output_verbatim = \&_output_para;

*output_item = \&_output_para;

method _output_para ( $para,
   :$margin //= 0,
   :$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} ) {
      my $width = max map { length $_ } @lines;
      $_ .= " " x ( $width - length $_ ) for @lines;
   }

   $margin += ( $typestyle{margin} // 0 );

   foreach my $line ( @lines ) {
      length $line or defined $leader or
         ( $self->say() ), next;

      my $width = $_TERMWIDTH - $margin - $indent;

      while( length $line or defined $leader ) {
         my $part;
         if( length($line) > $width ) {
            if( substr($line, 0, $width) =~ m/(\s+)\S*$/ ) {
               my $partlen = $-[1];
               my $chopat = $+[1];

               $part = $line->substr( 0, $partlen );
               $line->set_substr( 0, $chopat, "" );
            }
            else {
               die "ARGH: notsure how to trim this one\n";
            }
         }
         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;
            }

            undef $leader;
         }
         else {
            $prefix .= " "x$indent;
         }

         $self->say( $prefix, $part );
      }
   }



( run in 1.033 second using v1.01-cache-2.11-cpan-39bf76dae61 )