# -*-perl-*- # $Id: toc.wrt 5748 2008-12-02 20:10:35Z mnodine $ # Copyright (C) 2002-2005 Freescale Semiconductor, Inc. # Distributed under terms of the Perl license, which is the disjunction of # the GNU General Public License (GPL) and the Artistic License. =pod =begin reST =begin Description This writer creates table of content (TOC) for one or more reStructuredText documents. The output includes section headers from the source files organized into lists, one per top level section. The TOC entries are references to the corresponding sections in the source documents. This writer uses the following output defines: -W depth= Specify the depth of TOC, with 1 corresponding to the top level sections (default is 99). -W exclude-top=<0|1> Specify whether the top level section headers should be excluded from the TOC (default is 0, do not exclude). This has no effect if ``-W symbol=''`` is present. -W file-suffix= Specify a file suffix to be used as the targets of TOC entries. It is either a string or a comma-separated list of "ext1=ext2" pairs where any file with extension ext1 has it mapped to ext2. This option allows generating a table of contents for multiple documents where more than one extension is needed (e.g., .html and .xhtml). Default is "html". -W filename-ext= Specify an extension to the filename, (e.g. "_main") so the file location of targets becomes . (default is ""). -W include-noheader=<0|1> If set to 1, the writer includes in the TOC the document source bare name for any document that does not have a header (default is 0, do not include). -W parent-role= If specified, items which have sublists are created as interpreted text with the given role. The role must be defined elsewhere. -W symbol= If specified, the writer uses the symbol to create additional headers for the document titles. (default is "", no headers created). This output define overwrites ``-W exclude_top=1``. If a document does not have a title, use the document source bare name instead. -W top-in-list=<0|1> Specify whether the top level section headers should be part of the list with other section headers (default is 0, top level header being outside of the list). This has no effect if the top level headers are excluded. =end Description =end reST =cut sub BEGIN = { # My -W flags use vars qw($depth $exclude_top $file_suffix $filename_ext $include_noheader $parent_role $symbol $top_in_list); # Run-time globals use vars qw($TOCSTR $TGTSTR $INCLUDE_TOP @FINAL_TOP_SECS $SFX_SUB $DEFAULT_SFX); # Defaults for -W flags $file_suffix = 'html' unless defined $file_suffix; $filename_ext = '' unless defined $filename_ext; $depth = 99 unless defined $depth && $depth > 0; $symbol = "" unless defined $symbol; $include_noheader = 0 unless defined $include_noheader; $exclude_top = 0 unless defined $exclude_top; $top_in_list = 0 unless defined $top_in_list; ($TOCSTR, $TGTSTR) = ('') x 2; # Preprocess $file_suffix into a subroutine my @sfx_pairs = split /[=,]/, $file_suffix; unshift @sfx_pairs, '.*?' if @sfx_pairs == 1; my %sfx_to = @sfx_pairs; my @sfx_from = @sfx_pairs[map(2*$_, 0 .. ($#sfx_pairs >> 1))]; my $sfx_sub = 'sub { $_[0] =~ s/\.(?:' . join('|', map("($_)", @sfx_from)) . ')$/"$filename_ext." . (' . join(' : ', map("\$$_ ? '$sfx_to{$sfx_from[$_-1]}'", 1 .. @sfx_from), "'')") . '/e }'; $SFX_SUB = eval $sfx_sub; die $@ if $@; $DEFAULT_SFX = $sfx_to{$sfx_from[0]}; } sub END { $INCLUDE_TOP = 1 unless ($exclude_top && !$symbol); for (my $i = 0; $i <= $#FINAL_TOP_SECS; $i++) { my %topSecs = %{$FINAL_TOP_SECS[$i]}; my $title = $topSecs{title}; if ($symbol) { $title =~ s/([\`\_\*\|\\])/\\$1/go; $TOCSTR .= "$title\n" . $symbol x length($title) . "\n\n"; } foreach (@{$topSecs{sections}}) { writeTOC($_, 0, $topSecs{filename}); $TOCSTR .= "\n"; } } print $TOCSTR, "\n", $TGTSTR; } sub writeTOC { my ($secref, $level, $source) = @_; return if ($level >= $depth); my ($name, $id, $subref) = @$secref; $name =~ s//\\\>/g; $name =~ s/^\s*(.*?)\s*$/$1/; my $is_parent = (defined $subref && @$subref && $level < $depth - 1 && ($level > 0 || ($INCLUDE_TOP && $top_in_list))); if ($level > 0) { my $indent = ($INCLUDE_TOP && $top_in_list)? $level : $level - 1; $indent = " " x $indent; $TOCSTR .= $indent; } if ($level > 0 || $INCLUDE_TOP) { # Write the top level section as header if required by user if ($level == 0 && !$top_in_list) { # Top level entry is part of a separate paragraph $TOCSTR .= "`$name`__\n\n"; } else { $TOCSTR .= $is_parent && defined $parent_role ? "* :$parent_role:`\\ `$name`__`\n" : "* `$name`__\n"; } $TOCSTR .= "\n" if $is_parent; $TGTSTR .= $id ? "__ $source#$id\n" : "__ $source\n"; } foreach (@$subref) { writeTOC($_, $level + 1, $source); } } phase PROCESS = { sub \#PCDATA = { my ($dom, $str) = @_; return $dom->{text}; } # Return the generated section number, without the non-breaking # spaces generated at the end. sub generated = { my ($dom, $str) = @_; # Note: This code will have to change if the number of # non-breaking spaces inserted is some number other than 3. $str =~ s/[\xa0\xc2]+$/ /; #$str =~ s/(.*)\1\1$/ /; return $str; } # Return the title. sub title = { my ($dom, $str) = @_; chomp($str); return $str; } sub section = { my ($dom, $str) = @_; my $parent = $dom->parent(); # uncoverable branch false note:ids should always be set push(@{$parent->{_toc}{subSections}}, [$str, $dom->{attr}{ids}[0], $dom->{_toc}{subSections}]) if $dom->{attr}{ids}; return; } sub document = { my ($dom, $str) = @_; my $fileName = $dom->{attr}{source}; $fileName .= "$filename_ext.$DEFAULT_SFX" unless &$SFX_SUB($fileName); my $firstHeader = defined $dom->{_toc}{subSections} ? ${${$dom->{_toc}{subSections}}[0]}[0] : ''; # If there was a title produced by a removed section, use it $str = $dom->{attr}{title} || '' unless $str; $str =~ s/[\xa0\xa2]+/ /; if ($str && ($str ne $firstHeader)) { # If the top-level section has been removed (due to # transformation) restore it in the TOC. $dom->{_toc}{subSections} = [[$str, undef, \@{$dom->{_toc}{subSections}}]]; } elsif ($str) { # If the top-level section header is the same as document # title, make the top-level TOC entry point to the # document itself. ${${$dom->{_toc}{subSections}}[0]}[1] = undef; } else { # Document doesn't have a title. Use bare source name. ($str = $fileName) =~ s/$filename_ext\.$file_suffix$//o; } if ($include_noheader && ($#{$dom->{_toc}{subSections}} < 0 || $firstHeader eq "Docutils System Messages")) { $dom->{_toc}{subSections} = [[$str, undef, \@{$dom->{_toc}{subSections}}]] } if (defined $dom->{_toc}{subSections}) { push @FINAL_TOP_SECS, {filename => $fileName, title => $str, sections => $dom->{_toc}{subSections}}; } return undef; } }