File Coverage

blib/lib/App/sdview/Parser/Man.pm
Criterion Covered Total %
statement 99 115 86.0
branch 20 28 71.4
condition 8 10 80.0
subroutine 17 19 89.4
pod 0 4 0.0
total 144 176 81.8


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-2022 -- leonerd@leonerd.org.uk
5              
6 3     3   473144 use v5.26;
  3         21  
7 3     3   17 use warnings;
  3         7  
  3         80  
8 3     3   16 use utf8;
  3         6  
  3         24  
9              
10 3     3   1333 use Object::Pad 0.800;
  3         19178  
  3         212  
11              
12             package App::sdview::Parser::Man 0.13;
13             class App::sdview::Parser::Man
14             :does(App::sdview::Parser)
15 2     2   1188 :strict(params);
  2         7  
  2         100  
16              
17 3     3   2394 use Parse::Man::DOM 0.03;
  3         58920  
  3         112  
18              
19 3     3   29 use String::Tagged;
  3         19  
  3         100  
20              
21 3     3   20 use constant sort_order => 30;
  3         7  
  3         9169  
22              
23 0         0 sub find_file ( $class, $name )
24 0     0 0 0 {
  0         0  
  0         0  
25 0         0 open my $f, "-|", "man", "--path", $name;
26 0 0       0 my $file = <$f>; chomp $file if defined $file;
  0         0  
27 0         0 close $f;
28 0 0       0 $? == 0 or return undef;
29 0         0 return $file;
30             }
31              
32 2         5 sub can_parse_file ( $class, $file )
33 2     2 0 6690 {
  2         3  
  2         4  
34 2         21 return $file =~ m/\.[0-9](pm)?(\.gz)?/n;
35             }
36              
37             field @_paragraphs;
38              
39 0         0 method parse_file ( $fh )
  0         0  
  0         0  
40 0     0 0 0 {
41 0         0 return $self->_parse( Parse::Man::DOM->new->from_file( $fh ) );
42             }
43              
44 11         19 method parse_string ( $str )
  11         19  
  11         15  
45 11     11 0 28 {
46 11         76 return $self->_parse( Parse::Man::DOM->new->from_string( $str ) );
47             }
48              
49 11         19 method _parse ( $dom )
  11         20  
  11         14  
50 11     11   52420 {
51             # Not much we can do with the meta sections
52              
53 11         20 @_paragraphs = ();
54              
55 11         33 foreach my $para ( $dom->paras ) {
56 32         424 my $type = $para->type;
57 32 50       155 if( my $code = $self->can( "_handle_$type" ) ) {
58 32         84 $self->$code( $para );
59             }
60             else {
61 0         0 print STDERR "TODO: para->type = $type\n";
62             }
63             }
64              
65 11         145 return @_paragraphs;
66             }
67              
68             my %FONTTAGS = (
69             B => { bold => 1 },
70             I => { italic => 1 },
71             CW => { monospace => 1 },
72             );
73              
74 33         47 sub _chunklist_to_taggedstring ( $chunks, %opts )
75 33     33   244 {
  33         80  
  33         50  
76 33         81 my $ret = String::Tagged->new;
77              
78 33   100     501 my $linefeed = $opts{linefeed} // " ";
79              
80 33         67 foreach my $chunk ( $chunks->@* ) {
81 55         1037 my %tags;
82              
83 55   100     125 my $font = $chunk->font // "";
84 55 100       317 %tags = $FONTTAGS{$font}->%* if $FONTTAGS{$font};
85              
86 55         111 my $text = $chunk->text;
87 55 50       207 $text = "\n" if $chunk->is_space;
88 55 100       208 $text = $linefeed if $chunk->is_linebreak;
89 55 50       190 $text = "\n\n" if $chunk->is_break;
90              
91 55         243 $ret->append_tagged( $text, %tags );
92             }
93              
94             # Trim trailing space
95 33 100       1364 $ret =~ m/([ \n]+)$/ and
96             $ret->set_substr( $-[1], $+[1]-$-[1], "" );
97              
98 33         602 return $ret;
99             }
100              
101 5         9 method _handle_heading ( $para )
  5         8  
  5         8  
102 5     5   15 {
103 5         20 push @_paragraphs, App::sdview::Para::Heading->new(
104             level => $para->level,
105             text => String::Tagged->new( $para->text ),
106             );
107             }
108              
109 11         16 method _handle_plain ( $para )
  11         37  
  11         14  
110 11     11   27 {
111 11         62 push @_paragraphs, App::sdview::Para::Plain->new(
112             text => _chunklist_to_taggedstring( [ $para->body->chunks ] ),
113             indent => $para->indent,
114             );
115             }
116              
117 6         11 method _handle_term ( $para )
  6         9  
  6         9  
118 6     6   15 {
119 6         12 my $list;
120 6 100 66     40 if( @_paragraphs and $_paragraphs[-1]->type eq "list-text" ) {
121 4         9 $list = $_paragraphs[-1];
122             }
123             else {
124 2         26 push @_paragraphs, $list = App::sdview::Para::List->new(
125             listtype => "text",
126             indent => 4,
127             );
128             }
129              
130 6         26 $list->push_item(
131             App::sdview::Para::ListItem->new(
132             listtype => "text",
133             term => _chunklist_to_taggedstring( [ $para->term->chunks ] ),
134             text => _chunklist_to_taggedstring( [ $para->definition->chunks ] )
135             )
136             );
137             }
138              
139 2         5 method _handle_example ( $para )
  2         4  
  2         4  
140 2     2   8 {
141 2         9 push @_paragraphs, App::sdview::Para::Verbatim->new(
142             text => _chunklist_to_taggedstring( [ $para->body->chunks ], linefeed => "\n" ),
143             );
144             }
145              
146 8         18 method _handle_indent ( $para )
  8         14  
  8         12  
147 8     8   19 {
148 8         13 my $listtype = "(plain)";
149 8 100       25 if( defined( my $marker = $para->marker ) ) {
150 6         33 $listtype = "text";
151 6 50       18 $listtype = "bullet" if $marker eq "•";
152             }
153              
154 8         18 my $list;
155 8 100 66     49 if( @_paragraphs and (
    100          
156             ( $listtype eq "(plain)" ? $_paragraphs[-1]->type =~ m/^list-/
157             : $_paragraphs[-1]->type eq "list-$listtype" ) ) ) {
158 6         11 $list = $_paragraphs[-1];
159             }
160             else {
161 2         15 push @_paragraphs, $list = App::sdview::Para::List->new(
162             listtype => $listtype,
163             indent => $para->indent,
164             );
165             }
166              
167 8 100       22 if( $listtype eq "(plain)" ) {
168 2         10 $list->push_item(
169             App::sdview::Para::Plain->new(
170             text => _chunklist_to_taggedstring( [ $para->body->chunks ] ),
171             indent => $para->indent,
172             )
173             );
174             }
175             else {
176 6         22 $list->push_item(
177             App::sdview::Para::ListItem->new(
178             listtype => $listtype,
179             text => _chunklist_to_taggedstring( [ $para->body->chunks ] ),
180             )
181             );
182             }
183             }
184              
185             0x55AA;