File Coverage

blib/lib/App/sdview.pm
Criterion Covered Total %
statement 29 76 38.1
branch 0 36 0.0
condition 0 16 0.0
subroutine 10 12 83.3
pod 0 2 0.0
total 39 142 27.4


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2021-2023 -- leonerd@leonerd.org.uk
5              
6 1     1   615 use v5.26;
  1         5  
7 1     1   6 use warnings;
  1         3  
  1         62  
8 1     1   7 use utf8;
  1         2  
  1         10  
9              
10 1     1   47 use Object::Pad 0.800;
  1         9  
  1         46  
11              
12             package App::sdview 0.20;
13             class App::sdview :strict(params);
14              
15 1     1   483 use Sublike::Extended 0.29 'method';
  1         22  
  1         9  
16              
17 1     1   67 use App::sdview::Style;
  1         3  
  1         33  
18 1     1   6 use App::sdview::Highlighter;
  1         2  
  1         61  
19              
20 1     1   7 use List::Keywords qw( first );
  1         3  
  1         11  
21              
22             =head1 NAME
23              
24             C - a terminal document viewer for Pod and other syntaxes
25              
26             =head1 SYNOPSIS
27              
28             =for highlighter perl
29              
30             use App::sdview;
31              
32             exit App::sdview->new->run( "some-file.pod" );
33              
34             =head1 DESCRIPTION
35              
36             This module implements a terminal-based program for viewing structured
37             documents. It currently understands Pod, some simple Markdown formatting, and
38             a basic understanding of nroff (for manpages). Future versions may expand on
39             these abilities, extending them or adding new formats.
40              
41             To actually use it, you likely wanted wanted to see the F script.
42              
43             =for highlighter
44              
45             $ sdview Some::Module
46              
47             $ sdview lib/Some/Module.pm
48              
49             $ sdview README.md
50              
51             $ sdview man/somelib.3
52              
53             Various output plugins exist. By default it will output a terminal-formatted
54             rendering of the document via the F pager, but it can also output
55             plaintext, Pod, Markdown.
56              
57             $ sdview Some::Module -o plain > module.txt
58              
59             $ sdview Some::Module -o Markdown > module.md
60              
61             =cut
62              
63             # Permit loaded output modules to override
64             our $DEFAULT_OUTPUT = "terminal";
65              
66             use Module::Pluggable
67 1         14 search_path => "App::sdview::Parser",
68             sub_name => "PARSERS",
69             inner => 0,
70 1     1   186 require => 1;
  1         3  
71              
72             use Module::Pluggable
73 1         6 search_path => "App::sdview::Output",
74             sub_name => "OUTPUTS",
75             inner => 0,
76 1     1   193 require => 1;
  1         1  
77              
78             # Must call this *before* ->run entersub so that DEFAULT_OUTPUT is overridden properly
79             my @OUTPUT_CLASSES = OUTPUTS();
80              
81 0           method run ( $file,
  0            
82             :$format = undef,
83             :$output //= $DEFAULT_OUTPUT,
84             :$highlight = 0,
85             :$output_options //= [],
86             %opts
87 0     0 0   ) {
  0            
  0            
88 0           my @PARSER_CLASSES = sort { $a->sort_order <=> $b->sort_order } PARSERS();
  0            
89              
90 0 0 0       if( ( $format // "" ) eq "?" ) {
91 0           say "Parser format types:";
92             $_->can( "format" ) and say " " . $_->format . " (provided by $_)"
93 0   0       for @PARSER_CLASSES;
94 0           exit 0;
95             }
96 0 0 0       if( ( $output // "" ) eq "?" ) {
97 0           say "Output format types:";
98             $_->can( "format" ) and say " " . $_->format . " (provided by $_)"
99 0   0       for @OUTPUT_CLASSES;
100 0           exit 0;
101             }
102              
103 0 0         if( -f( my $configpath = "$ENV{HOME}/.sdviewrc" ) ) {
104 0           App::sdview::Style->load_config( $configpath );
105             }
106              
107             my %output_options = map {
108 0 0         map { m/^(.*?)=(.*)$/ ? ( $1 => $2 ) : ( $_ => !!1 ) } split m/,/, $_;
  0            
  0            
109             } $output_options->@*;
110              
111 0           my $parser_class;
112              
113 0 0         if( defined $format ) {
114 0 0         $parser_class = first { $_->can( "format" ) and $_->format eq $format } @PARSER_CLASSES or
  0 0          
115             die "Unrecognised format name $format\n";
116             }
117              
118 0 0         if( !defined $file ) {
119 0           die "Require a FILE to read - such as doc.md or doc.pod\n";
120             }
121              
122 0 0         if( ! -f $file ) {
123 0           my $name = $file;
124              
125 0 0         foreach my $class ( $parser_class ? ( $parser_class ) : @PARSER_CLASSES ) {
126 0 0         defined( $file = $class->find_file( $name ) ) and
127             $parser_class = $class, last;
128             }
129              
130 0 0         defined $file or
131             die "Unable to find a file for '$name'\n";
132             }
133              
134 0   0       $parser_class //= do {
135 0 0         first { $_->can_parse_file( $file ) } @PARSER_CLASSES or
  0            
136             die "Unable to find a handler for $file\n";
137             };
138              
139 0 0         my $output_class = first { $_->can( "format" ) and $_->format eq $output } @OUTPUT_CLASSES or
  0 0          
140             die "Unrecognised output name $output\n";
141              
142 0           my @paragraphs = $parser_class->new->parse_file( $file );
143              
144 0 0         if( $highlight ) {
145 0           apply_highlights( $_ ) for @paragraphs;
146             }
147              
148             # TODO: unrecognised output option key names will not look very neat here
149 0           $output_class->new( %output_options )->output( @paragraphs );
150             }
151              
152             sub apply_highlights ( $para )
153 0     0 0   {
  0            
  0            
154 0 0 0       if( $para->type eq "verbatim" and defined( my $language = $para->language ) ) {
155 0           App::sdview::Highlighter->highlight_str( $para->text, $language );
156             }
157              
158 0 0         if( $para->type =~ m/^list-/ ) {
159 0           apply_highlights( $_ ) for $para->items;
160             }
161             }
162              
163             =head1 TODO
164              
165             =over 4
166              
167             =item *
168              
169             Add more formats. ReST perhaps. Maybe others too.
170              
171             =item *
172              
173             Improved Markdown parser. Currently the parser is very simple.
174              
175             =item *
176              
177             Also more structured file writers - ReST.
178              
179             =back
180              
181             =cut
182              
183             =head1 AUTHOR
184              
185             Paul Evans
186              
187             =cut
188              
189             0x55AA;