File Coverage

blib/lib/XML/Filter/EzPod.pm
Criterion Covered Total %
statement 96 96 100.0
branch 34 34 100.0
condition n/a
subroutine 10 10 100.0
pod 3 5 60.0
total 143 145 98.6


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             XML::Filter::EzPod - A SAX filter (for Pod::SAX) that makes writing Pod easier.
4              
5             =head1 SYNOPSIS
6              
7             my $p = Pod::SAX->new(
8             Handler => XML::Filter::EzPod->new(
9             Handler => XML::SAX::Writer->new(
10             Output => \$output
11             )
12             )
13             );
14              
15             =head1 DESCRIPTION
16              
17             Don't you just get sick of writing lists in pod? So much annoying vertical
18             whitespace! I got sick of it too. So this filter turns something like:
19              
20             * A bullet
21             * Point
22             ** With extra levels
23             ** Going
24             *** Up
25             ** and
26             * Down
27             ** And up again
28              
29             Into the appropriate SAX events as though the source were a POD list.
30              
31             =head1 SUPPORTED SYNTAX
32              
33             =head2 Itemized Lists
34              
35             Can be created with asterisks. These must be in the first column, and followed
36             by whitespace. Arbitrary asterisk levels are supported.
37              
38             Example:
39              
40             * an itemized
41             * list is created
42             * as follows
43             ** with possible further indents
44              
45             =head2 Ordered Lists
46              
47             Can be created with hashes (also known as the pound sign). These must be in the
48             first column and followed by whitespace. Arbitrary levels are supported.
49              
50             Example:
51              
52             # An ordered
53             # list is created
54             # as follows
55             ## with possible
56             ## further indents
57              
58             =head2 The C Value
59              
60             It is possible to set the value normally given in POD's C<=over N> parameter
61             using the greater-than sign before your list:
62              
63             >6
64             * A bulleted list
65             * equivalent to =over 6
66              
67             A single greater-than sign on its own will set the C to I<-1> which
68             is useful in conjunction with AxKit2's I plugin, indicating the points
69             should appear incrementally.
70              
71             A double greater-than sign will set the C to I<-2>. Again, for
72             spod5, indicating the points should appear incrementally with the first point
73             shown automatically.
74              
75             =head1 LICENSE
76              
77             This is free software. You may use it and redistribute it under the same terms
78             as perl itself.
79              
80             =head1 AUTHOR
81              
82             Matt Sergeant,
83              
84             =head1 BUGS
85              
86             The test suite doesn't actually test anything - it's there for me as a visual
87             inspection to check everything's working.
88              
89             =cut
90              
91             package XML::Filter::EzPod;
92              
93 5     5   181824 use strict;
  5         13  
  5         273  
94              
95             our $VERSION = '1.2';
96              
97 5     5   1461 use XML::SAX::Base;
  5         19847  
  5         121  
98              
99 5     5   44 use base qw(XML::SAX::Base);
  5         12  
  5         5560  
100              
101             sub new {
102 4     4 0 1148 my $class = shift;
103 4         32 my $self = $class->SUPER::new(@_);
104 4         189 $self->{in_paragraph} = 0;
105 4         10 $self->{cur_element} = '';
106 4         10 $self->{ol_level} = 0;
107 4         11 $self->{ul_level} = 0;
108 4         7 $self->{indent} = 4;
109 4         12 $self->{listopen} = 0;
110 4         51 return $self;
111             }
112              
113             sub start_element {
114 25     25 1 8800 my ($self, $el) = @_;
115            
116 25         48 $self->{cur_element} = $el->{LocalName};
117 25 100       80 if ($el->{LocalName} eq 'para') {
    100          
118 16         32 $self->{in_paragraph}++;
119             }
120             elsif ($self->{in_paragraph}) {
121             # paragraph sub-element (e.g. I<...>)
122 1         1 $self->{in_paragraph}++;
123             }
124            
125 25         133 $self->SUPER::start_element($el);
126             }
127              
128             sub end_element {
129 25     25 1 1170 my ($self, $el) = @_;
130            
131 25 100       74 if ($self->{in_paragraph}) {
132 17         23 $self->{in_paragraph}--;
133             }
134 25 100       60 if ($self->{in_paragraph} == 0) {
135             # close all lists
136 24         51 $self->close_list;
137 24         56 for (1 .. $self->{ol_level}) {
138             #print "\n";
139 2         79 $self->SUPER::end_element(_element('orderedlist'));
140 2         94 $self->{ol_level} = 0;
141             }
142 24         46 for (1 .. $self->{ul_level}) {
143             #print "\n";
144 6         16 $self->SUPER::end_element(_element('itemizedlist'));
145 6         271 $self->{ul_level} = 0;
146             }
147 24         37 $self->{indent} = 4;
148             }
149            
150 25         103 $self->SUPER::end_element($el);
151             }
152              
153             sub close_list {
154 55     55 0 65 my $self = shift;
155 55 100       128 if ($self->{listopen}) {
156 31         51 $self->SUPER::end_element(_element('listitem', 1));
157 31         2910 $self->{listopen} = 0;
158             }
159             }
160              
161             sub characters {
162 50     50 1 7036 my ($self, $data) = @_;
163            
164 50 100       138 if ($self->{cur_element} eq 'para') {
165             #print "Chars: $data->{Data}\n";
166 31 100       118 if ($data->{Data} =~ /^[\*\#]/m) {
167             # likely candidate for turning into bullet points
168 6         27 my @lines = split(/\n/, $data->{Data});
169 6         16 for my $line (@lines) {
170 35 100       121 if ($line =~ /^>(>?)$/) {
    100          
171 2 100       5 if ($1) {
172 1         3 $self->{indent} = "-2";
173             }
174             else {
175 1         2 $self->{indent} = "-1";
176             }
177 2         4 next;
178             }
179             elsif ($line =~ /^>(\d+)$/) {
180 1         3 $self->{indent} = $1;
181 1         2 next;
182             }
183 32 100       138 if ($line =~ s/^(\*+)\s//) {
    100          
184 21         42 $self->close_list;
185 21         38 my $depth = length($1);
186 21 100       46 if ($self->{ol_level}) {
187 1         4 for (1 .. $self->{ol_level}) {
188             #print "\n";
189 1         3 $self->SUPER::end_element(_element('orderedlist'));
190             }
191 1         54 $self->{ol_level} = 0;
192             }
193 21         40 my $diff = $depth - $self->{ul_level};
194 21         38 while ($diff) {
195 16 100       44 if ($diff > 0) {
196             #print "
    \n";
197 12         28 $self->SUPER::start_element(
198             _add_attrib(_element('itemizedlist'), indent_width => $self->{indent})
199             );
200 12         831 $diff--;
201             }
202             else {
203             #print "\n";
204 4         41 $self->SUPER::end_element(_element('itemizedlist', 1));
205 4         166 $diff++;
206             }
207             }
208 21         31 $self->{ul_level} = $depth;
209             #print "$line\n";
210 21         73 $self->SUPER::start_element(_element('listitem'));
211 21         1154 $self->SUPER::characters({ Data => $line });
212 21         194 $self->{listopen} = 1;
213             }
214             elsif ($line =~ s/^(\#+)\s//) {
215 10         19 $self->close_list;
216 10         20 my $depth = length($1);
217 10 100       24 if ($self->{ul_level}) {
218 1         3 for (1 .. $self->{ul_level}) {
219 2         43 $self->SUPER::end_element(_element('itemizedlist'));
220             }
221 1         43 $self->{ul_level} = 0;
222             }
223 10         18 my $diff = $depth - $self->{ol_level};
224 10         20 while ($diff) {
225 7 100       16 if ($diff > 0) {
226             #print "
    \n";
227 5         10 $self->SUPER::start_element(
228             _add_attrib(_element('orderedlist'), indent_width => $self->{indent})
229             );
230 5         348 $diff--;
231             }
232             else {
233             #print "\n";
234 2         7 $self->SUPER::end_element(_element('orderedlist', 1));
235 2         102 $diff++;
236             }
237             }
238 10         16 $self->{ol_level} = $depth;
239             #print "$line\n";
240 10         39 $self->SUPER::start_element(_element('listitem'));
241 10         589 $self->SUPER::characters({ Data => $line });
242 10         94 $self->{listopen} = 1;
243             }
244             else {
245 1         6 $self->SUPER::characters({ Data => $line });
246             }
247             }
248 6         26 return;
249             }
250             }
251            
252 44         194 $self->SUPER::characters($data);
253             }
254              
255             sub _element {
256 96     96   126 my ($name, $end) = @_;
257             return {
258 96 100       604 Name => $name,
259             LocalName => $name,
260             $end ? () : (Attributes => {}),
261             NamespaceURI => '',
262             Prefix => '',
263             };
264             }
265              
266             sub _add_attrib {
267 17     17   27 my ($el, $name, $value) = @_;
268            
269 17         90 $el->{Attributes}{"{}$name"} =
270             {
271             Name => $name,
272             LocalName => $name,
273             Prefix => "",
274             NamespaceURI => "",
275             Value => $value,
276             };
277            
278 17         86 return $el;
279             }
280              
281             1;