Changeset 1638
- Timestamp:
- 02/05/05 14:19:45 (4 years ago)
- Files:
-
- trunk/swish_website/Plugin/My/POD.pm (modified) (12 diffs)
- trunk/swish_website/bin/build (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/swish_website/Plugin/My/POD.pm
r1633 r1638 4 4 use Pod::POM::View::HTML; 5 5 use base 'Template::Plugin'; 6 7 my %split_by = map {"head".$_ => 1} 1..4; 6 use vars qw( $stash ); # for debugging links 7 8 if ( my $ file = shift ) { 9 warn "Testing links\n"; 10 My::pod::test(); 11 } 12 8 13 9 14 my $view_mode = 'Pod::POM::View::HTML'; # The view module … … 27 32 my ( $class, $context, $content ) = @_; 28 33 29 30 # Grab pod index variable 31 my $stash = $context->stash; 32 my $page = $stash->get( 'page.id' ); 34 $stash = $context->stash; 35 33 36 my $template_name = $stash->get( 'template.name' ); 34 37 38 # Enable output of debugging messages from Pod::POM 35 39 my $warn = sub { warn "[$template_name]: @_\n" } 36 40 if $stash->get( 'self.config.verbose' ); 37 41 38 my $parser = Pod::POM->new( warn => $warn ); # Make this a coderef to report name 42 my $parser = Pod::POM->new( warn => $warn ); 43 44 45 # Parse the pod into a Pod::POM tree 39 46 my $pom = $parser->parse_text( $content ); 40 47 41 combine_verbatim_sections_hack( $pom ); # merge sequential <pre> sections into one 42 43 44 my @sections = $pom->head1; # get pod into sections 45 46 47 # Structure for returning info to template 48 49 my %data = ( 50 pom => $pom , 51 view => 'My::Pod::View::HTML', 52 sections => \@sections, 53 podparts => [ slice_by_head(@sections) ], 54 toc => fetch_toc( \@sections ), 55 ); 56 57 58 # Get title of document 48 49 # Merge sequential verbatim (<pre>) sections into one 50 combine_verbatim_sections_hack( $pom ); 51 52 53 my @sections = $pom->head1; # get pod into sections 54 55 # Get title of document and shift off first NAME section 56 57 my $doc_title; 59 58 if ( @sections && $sections[0]->title =~ /NAME/ ) { 60 $data{title} = ( shift @sections )->content; 61 $data{title} =~ s/^\s*|\s*$//sg; # strip; 62 } 59 $doc_title = ( shift @sections )->content; 60 $doc_title =~ s/^\s*|\s*$//sg; # strip; 61 } 62 63 63 64 64 … … 68 68 if ( my $cache = $stash->get( 'toc_cache' ) ) { 69 69 $cache->{ $template_name } = { 70 page => $ page,71 title => $d ata{title},72 abstract => fetch_abstract( $data{sections}),70 page => $stash->get( 'page.id' ), 71 title => $doc_title, 72 abstract => fetch_abstract( \@sections ), 73 73 }; 74 74 }; 75 75 76 76 77 # Structure for returning info to template 78 # the "toc" is the table of contents for the top of the page 79 # the "podparts" is the document broken into sequential parts by 80 # =head* This allows the template to wrap each section in a <div> and 81 # therefore makes indexing each section with swish-e easy -- allows targeted searches 82 83 my %data = ( 84 pom => $pom, 85 view => 'My::Pod::View::HTML', 86 toc => fetch_toc( @sections ), 87 podparts => [ slice_by_head(@sections) ], # Must be lasts because modifies tree 88 title => $doc_title, 89 ); 90 91 92 77 93 return \%data; 78 94 } … … 81 97 82 98 #----------------------------------------------------------------------------- 99 # This function is modified from Stas' DocSet 100 # It flattens the recursive structure of the tree into a list of sections by =head 101 # 102 # Pass in: 103 # @sections = list of head1 sections 104 # 105 # Returns: 106 # flat list of sections for each headX section 107 # 108 # Note: 109 # This MODIFIES the original tree! Should probably clone and build a new tree. 110 # 111 # This is useful for printing and wrapping each headX section in, say, <div> 83 112 84 113 sub slice_by_head { 85 my @sections = @_; 114 my @sections = @_; # these are the sections at the current level. 86 115 my @body = (); 116 87 117 for my $node (@sections) { 88 118 my @next = (); 89 119 # assumption, after the first 'headX' section, there can only 90 120 # be other 'headX' sections 91 my $count = scalar $node->content; 121 122 # Look for first =headX seciton and chop of the content there 123 # then process that new =headX section again 124 92 125 my $id = -1; 93 126 for ($node->content) { 94 127 $id++; 95 next unless exists $split_by{ $_->type }; 128 # Keep content until a =head section is found. Then end current content 129 # section at that point. 130 131 next unless $_->type =~ /^head\d$/; 132 133 # Modify original content to just include the nodes up to the first =head 96 134 @next = splice @{$node->content}, $id; 97 135 last; 98 136 } 137 138 # combine all =head nodes (flatten) 99 139 push @body, $node, slice_by_head(@next); 100 140 } 101 141 return @body; 102 142 } 143 144 #--------------------------------------------------------------------------- 145 # Merge verbatim sequential <pre> sections together 103 146 104 147 sub combine_verbatim_sections_hack { … … 124 167 125 168 169 170 #---------------------------------------------------------------------------- 171 # Grabs the first paragraph from either DESCRIPTION or OVERVIEW -- whichever comes first 172 173 126 174 sub fetch_abstract { 127 175 my ( $sections ) = @_; … … 136 184 137 185 186 #--------------------------------------------------------------------------------- 187 # Walk tree building up a TOC 188 138 189 sub fetch_toc { 139 my ( $sections ) = @_;190 my ( @sections ) = @_; 140 191 141 192 my @toc = (); 142 193 my $level = 1; 143 for my $node (@ $sections) {194 for my $node (@sections) { 144 195 push @toc, render_toc_level($node, $level); 145 196 } … … 151 202 sub render_toc_level { 152 203 my( $node, $level) = @_; 153 my $title = $node->title ;204 my $title = $node->title->present($view_mode); 154 205 my $anchor = My::Pod::View::HTML->escape_name( $title ); 155 206 156 207 my %toc_entry = ( 157 title => $title ->present($view_mode), # run the formatting if any208 title => $title, 158 209 link => "#$anchor", 159 210 ); … … 173 224 } 174 225 226 227 228 #---------------- Package to override Pod::POM::View::HTML ---------------- 229 175 230 package My::Pod::View::HTML; 176 231 use strict; 177 232 use warnings; 178 233 use base 'Pod::POM::View::HTML'; 179 180 sub view_head1 { 181 my ($self, $head1) = @_; 182 return "<h1>" . $self->anchor($head1->title) . "</h1>\n\n" . 183 $head1->content->present($self); 184 } 185 186 sub view_head2 { 187 my ($self, $head2) = @_; 188 return "<h2>" . $self->anchor($head2->title) . "</h2>\n\n" . 189 $head2->content->present($self); 190 } 191 192 sub view_head3 { 193 my ($self, $head3) = @_; 194 return "<h3>" . $self->anchor($head3->title) . "</h3>\n\n" . 195 $head3->content->present($self); 196 } 197 198 sub view_head4 { 199 my ($self, $head4) = @_; 200 return "<h4>" . $self->anchor($head4->title) . "</h4>\n\n" . 201 $head4->content->present($self); 202 } 203 234 use HTML::Entities; 235 use Template::Filters; 236 use File::Basename; 237 238 use vars qw( %targets @links ); 239 240 241 #-------------- For checking that all pod links work ---------------- 242 sub save_link { 243 my ($self, $href) = @_; 244 245 246 my $out_file = basename( $My::POD::stash->get( 'this.out_file' ) ); # for debugging links 247 248 249 push @links, { 250 href => $href, 251 on_page => $out_file, 252 }; 253 } 254 255 sub save_target { 256 my ($self, $target) = @_; 257 my $out_file = basename( $My::POD::stash->get( 'this.out_file' ) ); 258 $targets{$out_file}{$target}++; 259 } 260 261 sub validate_links { 262 my $all_files_processed = $My::POD::stash->get( 'self.config.all'); 263 264 for my $link ( @links ) { 265 my ($name, $fragment) = split /#/, $link->{href}, 2; 266 267 # If processing all files then can check for page links 268 if ( $all_files_processed ) { 269 if ( $name && !$targets{$name} ) { 270 warn "Link to page [$name] from $link->{on_page} could not be resolved\n"; 271 next; 272 } 273 } else { 274 next if $name && $name ne $link->{on_page}; # can't check (unless links are cached in the future 275 } 276 277 $name ||= $link->{on_page}; # default to current page; 278 279 # Check page link 280 unless ( $targets{ $name } ) { 281 warn "Link on page $link->{on_page} to page $name is unresolved\n"; 282 next; 283 } 284 next unless $fragment; 285 286 # And check frag if one 287 unless ( $targets{ $name}{ $fragment } ) { 288 warn "Link [$link->{href}] on page $link->{on_page} could not be resolved\n"; 289 } else { 290 warn "Link $link->{href} on page $link->{on_page} has multiple targets\n" 291 if $targets{$name}{$fragment} > 1; 292 } 293 294 295 } 296 297 @links = (); 298 %targets = (); 299 } 300 301 302 #---------------- Override Pod::POM --------------------------------- 303 304 sub view_head1 { shift->show_head( @_ ) }; 305 sub view_head2 { shift->show_head( @_ ) }; 306 sub view_head3 { shift->show_head( @_ ) }; 307 sub view_head4 { shift->show_head( @_ ) }; 308 309 sub show_head { # all all child nodes 310 my ($self, $head) = @_; 311 my $level = substr( $head->type, -1 ); 312 return "\n<h$level>" . $self->anchor($head->title) . "</h$level>\n\n" . 313 $head->content->present($self); 314 } 315 316 317 #----------------- <a name="foo"></a>foo --------------------- 204 318 sub anchor { 205 319 my($self, $title) = @_; 206 my $text = $title->present($self); 207 $text = $self->escape_name( $text ); 208 return qq[<a name="$text"></a>$title]; 209 } 210 211 # This just adds a class attribute 212 sub view_verbatim { 320 my $text = $self->escape_name( $title->present($self) ); 321 my $t = Template::Filters::html_filter( $title ); 322 $self->save_target( $text ); 323 return qq[<a name="$text"></a>$t]; 324 } 325 326 #----------------- prepare text for name="" -------------------- 327 # This first unescapes any entities, and then removes all non-word chars 328 # and finally returns all lower case; 329 330 331 sub escape_name { 213 332 my ($self, $text) = @_; 214 return '' unless $text; 215 for ($text) { 216 s/&/&/g; 217 s/</</g; 218 s/>/>/g; 219 } 220 221 return qq{<pre class="pre-section">$text</pre>\n}; 222 } 223 333 334 # Comes in already HTML escaped -- well, kind of, quotes are not escaped. 335 my $plain = decode_entities( $text ); 336 337 # Not much is allowed in the name="" attribute, so just hack away: 338 $plain =~ s/\W+/_/g; 339 return lc $plain; 340 } 341 342 343 #----------------- Fixup L<> links in pod ------------------------------- 224 344 225 345 # Thes first two fixup L<> links. 226 346 # Pod::POM doesn't provide a way to get at the fragment for escaping, 227 # so do it the hard way here.347 # so let Pod::POM deal with it and then then update 228 348 229 349 sub view_seq_link { … … 231 351 my $url = $self->SUPER::view_seq_link( @_ ); 232 352 return unless $url; 233 $url = $1 . $self->escape_name($2) . $3 if $url =~ /^([^#]+#)([^"]+)(.+)$/; 234 $url =~ s/#item_/#/; 353 354 return unless $url =~ /href="(.+)(?=">)/; 355 my ( $href, $fragment ) = split /#/, $1, 2; 356 357 $href = '' unless defined $href; 358 $href .= '#'. $self->escape_name( $fragment ) if defined $fragment; 359 360 $self->save_link( $href ); # for checking that they all go some place. 361 362 # Now replace the href into the original string 363 364 $url =~ s/href=".+(?=">)/href="$href/; 365 235 366 return $url; 236 367 } 237 368 238 # This needs work -- Pod::POM doesn't uri escape the href, for one thing. 239 # This converts the file name part. Can't call esacpe_name(), so do uri240 # escape -- but that will not validate with xhtml in some cases. So this i241 # not really correct. 369 370 # This allows fixing up links to *other* pages. In our case, they are mostly to 371 # other pod pages, which are now lower case and end in .html. 372 242 373 243 374 sub view_seq_link_transform_path { 244 375 my ( $self, $link ) = @_; 245 return $self->escape_uri( lc $link ) . '.html'; 246 } 247 248 # Returns $text as uri-escaped string 249 sub escape_uri { 250 my ($self, $text) = @_; 251 $text =~ s/\n/ /g; 252 return Template::Filters::uri_filter( $text ); 253 } 254 255 sub escape_name { 256 my ($self, $text) = @_; 257 $text =~ s/\W+/_/g; 258 return lc $text; 259 } 260 261 262 # Pod::POM::View::HTML: 263 # view_seq_link is broken in many ways 264 # L<Foo Bar> ends up as a section due to spaces 265 # L</foo> or L<|/foo> don't work, but L<foo|foo> does work 266 # 267 # 268 # 376 return lc $link . '.html'; 377 } 378 269 379 270 380 # Modified version of item display that removes the item_ prefix and only takes the first … … 276 386 # 277 387 # so we take only the first word. But that breaks if linking to multi-word item. 388 # 278 389 279 390 sub view_item { … … 291 402 $anchor =~ s/\s+.*$//; # strip all trailing stuff from first space on 292 403 $anchor = $self->escape_name( $anchor ); 404 405 $self->save_target( $anchor ); 293 406 $title = qq{<a name="$anchor"></a><b>$title</b>}; 294 407 } … … 302 415 303 416 417 package My::pod; 418 use strict; 419 use warnings; 420 421 sub test { 422 423 # From perldoc perlpod: 424 # 425 my @l_tests = ( 426 { 427 in => ['L<name>'], 428 out => '<a href="name.html">name</a>', 429 }, 430 431 { 432 in => [ 'L<name/"section here">', 'L<name/section here>', ], 433 out => '<a href="name.html#section_here">section here</a>', 434 }, 435 436 { 437 in => ['L</"section here">', 'L</section here>', 'L<"section here">', ], 438 out => '<a href="#section_here">section here</a>', 439 }, 440 441 442 443 { in => ['L<text here|name>'], 444 out => '<a href="name.html">text here</a>', 445 }, 446 447 { 448 in => ['L<text|name/"sec here">', 'L<text|name/sec here>'], 449 out => '<a href="name.html#sec_here">text</a>', 450 }, 451 452 { 453 in => ['L<text|/"sec">', 'L<text|/sec>', 'L<text|"sec">'], 454 out => '<a href="#sec">text</a>', 455 }, 456 457 { 458 in => ['L<http://host.name/some/path.html#fragment>'], 459 out => '<a href="http://host.name/some/path.html#fragment">http://host.name/some/path.html#fragment</a>', 460 }, 461 462 { 463 in => ['L<Link to $foo-E<gt> with & "quotes" and %funny !!? chars>'], 464 out => '<a href="#link_to_foo_with_quotes_and_funny_chars">Link to $foo-> with & "quotes" and %funny !!? chars</a>', 465 }, 466 { 467 in => ['L<Some & text|/Link to $foo-E<gt> with & "quotes" and %funny !!? chars>'], 468 out => '<a href="#link_to_foo_with_quotes_and_funny_chars">Some & text</a>', 469 }, 470 { 471 in => ['L<SWISH-RUN|SWISH-RUN>'], 472 out => '<a href="swish-run.html">SWISH-RUN</a>', 473 } 474 ); 475 476 477 for my $test ( @l_tests ) { 478 for my $input ( @{$test->{in}} ) { 479 480 my $pod = Pod::POM->new( warn => 1 )->parse_text( "=head1 Title\n\n$input" ); 481 my $out = My::Pod::Test::HTML->print( $pod ); 482 483 warn "input [$input]\n", 484 "output [$out]\n", 485 "test [$test->{out}]\n\n" 486 if $out ne $test->{out} || $ENV{VERBOSE}; 487 } 488 } 489 } 490 491 package My::Pod::Test::HTML; 492 use strict; 493 use warnings; 494 use base 'My::Pod::View::HTML'; 495 496 sub view_textblock { 497 my ($self, $text) = @_; 498 return $text; 499 } 500 501 sub view_pod { 502 my ($self, $pod) = @_; 503 return $pod->content->present($self); 504 } 505 506 sub view_head1 { 507 my ($self, $pod) = @_; 508 return $pod->content->present($self); 509 } 510 511 304 512 1; 305 513 trunk/swish_website/bin/build
r1624 r1638 407 407 # Create sub-cache for this source, if doesn't exist 408 408 # When processing a pod file the POD plugin will update its own cache entry 409 # in the hash. 410 411 my $this_toc_cache = $toc_cache->{$ key} ||= {};409 # in the hash. Cache by source directory 410 411 my $this_toc_cache = $toc_cache->{$src_dir} ||= {}; 412 412 413 413 … … 458 458 abslinks => $abslinks, 459 459 swish_version => $version, 460 out_file => $out_file, 460 461 }, 461 462 mode => 0644, … … 478 479 479 480 shift @$include_path; # remove source dir. 481 482 # Check all the L<> links 483 My::Pod::View::HTML::validate_links() 484 if $update_index and $config->verbose; 480 485 } 481 486
