📄 text.pm
字号:
# Pod::Text -- Convert POD data to formatted ASCII text.# $Id: Text.pm,v 3.8 2006-09-16 20:55:41 eagle Exp $## Copyright 1999, 2000, 2001, 2002, 2004, 2006# by Russ Allbery <rra@stanford.edu>## This program is free software; you may redistribute it and/or modify it# under the same terms as Perl itself.## This module converts POD to formatted text. It replaces the old Pod::Text# module that came with versions of Perl prior to 5.6.0 and attempts to match# its output except for some specific circumstances where other decisions# seemed to produce better output. It uses Pod::Parser and is designed to be# very easy to subclass.## Perl core hackers, please note that this module is also separately# maintained outside of the Perl core as part of the podlators. Please send# me any patches at the address above in addition to sending them to the# standard Perl mailing lists.############################################################################### Modules and declarations##############################################################################package Pod::Text;require 5.004;use strict;use vars qw(@ISA @EXPORT %ESCAPES $VERSION);use Carp qw(carp croak);use Exporter ();use Pod::Simple ();@ISA = qw(Pod::Simple Exporter);# We have to export pod2text for backward compatibility.@EXPORT = qw(pod2text);# Don't use the CVS revision as the version, since this module is also in Perl# core and too many things could munge CVS magic revision strings. This# number should ideally be the same as the CVS revision in podlators, however.$VERSION = 3.08;############################################################################### Initialization############################################################################### This function handles code blocks. It's registered as a callback to# Pod::Simple and therefore doesn't work as a regular method call, but all it# does is call output_code with the line.sub handle_code { my ($line, $number, $parser) = @_; $parser->output_code ($line . "\n");}# Initialize the object and set various Pod::Simple options that we need.# Here, we also process any additional options passed to the constructor or# set up defaults if none were given. Note that all internal object keys are# in all-caps, reserving all lower-case object keys for Pod::Simple and user# arguments.sub new { my $class = shift; my $self = $class->SUPER::new; # Tell Pod::Simple to handle S<> by automatically inserting . $self->nbsp_for_S (1); # Tell Pod::Simple to keep whitespace whenever possible. if ($self->can ('preserve_whitespace')) { $self->preserve_whitespace (1); } else { $self->fullstop_space_harden (1); } # The =for and =begin targets that we accept. $self->accept_targets (qw/text TEXT/); # Ensure that contiguous blocks of code are merged together. Otherwise, # some of the guesswork heuristics don't work right. $self->merge_text (1); # Pod::Simple doesn't do anything useful with our arguments, but we want # to put them in our object as hash keys and values. This could cause # problems if we ever clash with Pod::Simple's own internal class # variables. my %opts = @_; my @opts = map { ("opt_$_", $opts{$_}) } keys %opts; %$self = (%$self, @opts); # Initialize various things from our parameters. $$self{opt_alt} = 0 unless defined $$self{opt_alt}; $$self{opt_indent} = 4 unless defined $$self{opt_indent}; $$self{opt_margin} = 0 unless defined $$self{opt_margin}; $$self{opt_loose} = 0 unless defined $$self{opt_loose}; $$self{opt_sentence} = 0 unless defined $$self{opt_sentence}; $$self{opt_width} = 76 unless defined $$self{opt_width}; # Figure out what quotes we'll be using for C<> text. $$self{opt_quotes} ||= '"'; if ($$self{opt_quotes} eq 'none') { $$self{LQUOTE} = $$self{RQUOTE} = ''; } elsif (length ($$self{opt_quotes}) == 1) { $$self{LQUOTE} = $$self{RQUOTE} = $$self{opt_quotes}; } elsif ($$self{opt_quotes} =~ /^(.)(.)$/ || $$self{opt_quotes} =~ /^(..)(..)$/) { $$self{LQUOTE} = $1; $$self{RQUOTE} = $2; } else { croak qq(Invalid quote specification "$$self{opt_quotes}"); } # If requested, do something with the non-POD text. $self->code_handler (\&handle_code) if $$self{opt_code}; # Return the created object. return $self;}############################################################################### Core parsing############################################################################### This is the glue that connects the code below with Pod::Simple itself. The# goal is to convert the event stream coming from the POD parser into method# calls to handlers once the complete content of a tag has been seen. Each# paragraph or POD command will have textual content associated with it, and# as soon as all of a paragraph or POD command has been seen, that content# will be passed in to the corresponding method for handling that type of# object. The exceptions are handlers for lists, which have opening tag# handlers and closing tag handlers that will be called right away.## The internal hash key PENDING is used to store the contents of a tag until# all of it has been seen. It holds a stack of open tags, each one# represented by a tuple of the attributes hash for the tag and the contents# of the tag.# Add a block of text to the contents of the current node, formatting it# according to the current formatting instructions as we do.sub _handle_text { my ($self, $text) = @_; my $tag = $$self{PENDING}[-1]; $$tag[1] .= $text;}# Given an element name, get the corresponding method name.sub method_for_element { my ($self, $element) = @_; $element =~ tr/-/_/; $element =~ tr/A-Z/a-z/; $element =~ tr/_a-z0-9//cd; return $element;}# Handle the start of a new element. If cmd_element is defined, assume that# we need to collect the entire tree for this element before passing it to the# element method, and create a new tree into which we'll collect blocks of# text and nested elements. Otherwise, if start_element is defined, call it.sub _handle_element_start { my ($self, $element, $attrs) = @_; my $method = $self->method_for_element ($element); # If we have a command handler, we need to accumulate the contents of the # tag before calling it. if ($self->can ("cmd_$method")) { push (@{ $$self{PENDING} }, [ $attrs, '' ]); } elsif ($self->can ("start_$method")) { my $method = 'start_' . $method; $self->$method ($attrs, ''); }}# Handle the end of an element. If we had a cmd_ method for this element,# this is where we pass along the text that we've accumulated. Otherwise, if# we have an end_ method for the element, call that.sub _handle_element_end { my ($self, $element) = @_; my $method = $self->method_for_element ($element); # If we have a command handler, pull off the pending text and pass it to # the handler along with the saved attribute hash. if ($self->can ("cmd_$method")) { my $tag = pop @{ $$self{PENDING} }; my $method = 'cmd_' . $method; my $text = $self->$method (@$tag); if (defined $text) { if (@{ $$self{PENDING} } > 1) { $$self{PENDING}[-1][1] .= $text; } else { $self->output ($text); } } } elsif ($self->can ("end_$method")) { my $method = 'end_' . $method; $self->$method (); }}############################################################################### Output formatting############################################################################### Wrap a line, indenting by the current left margin. We can't use Text::Wrap# because it plays games with tabs. We can't use formline, even though we'd# really like to, because it screws up non-printing characters. So we have to# do the wrapping ourselves.sub wrap { my $self = shift; local $_ = shift; my $output = ''; my $spaces = ' ' x $$self{MARGIN}; my $width = $$self{opt_width} - $$self{MARGIN}; while (length > $width) { if (s/^([^\n]{0,$width})\s+// || s/^([^\n]{$width})//) { $output .= $spaces . $1 . "\n"; } else { last; } } $output .= $spaces . $_; $output =~ s/\s+$/\n\n/; return $output;}# Reformat a paragraph of text for the current margin. Takes the text to# reformat and returns the formatted text.sub reformat { my $self = shift; local $_ = shift; # If we're trying to preserve two spaces after sentences, do some munging # to support that. Otherwise, smash all repeated whitespace. if ($$self{opt_sentence}) { s/ +$//mg; s/\.\n/. \n/g; s/\n/ /g; s/ +/ /g; } else { s/\s+/ /g; } return $self->wrap ($_);}# Output text to the output device.sub output { my ($self, $text) = @_; $text =~ tr/\240\255/ /d; print { $$self{output_fh} } $text;}# Output a block of code (something that isn't part of the POD text). Called# by preprocess_paragraph only if we were given the code option. Exists here# only so that it can be overridden by subclasses.sub output_code { $_[0]->output ($_[1]) }############################################################################### Document initialization############################################################################### Set up various things that have to be initialized on a per-document basis.sub start_document { my $self = shift; my $margin = $$self{opt_indent} + $$self{opt_margin}; # Initialize a few per-document variables. $$self{INDENTS} = []; # Stack of indentations. $$self{MARGIN} = $margin; # Default left margin. $$self{PENDING} = [[]]; # Pending output. return '';}############################################################################### Text blocks############################################################################### This method is called whenever an =item command is complete (in other words,# we've seen its associated paragraph or know for certain that it doesn't have# one). It gets the paragraph associated with the item as an argument. If# that argument is empty, just output the item tag; if it contains a newline,# output the item tag followed by the newline. Otherwise, see if there's# enough room for us to output the item tag in the margin of the text or if we# have to put it on a separate line.sub item { my ($self, $text) = @_; my $tag = $$self{ITEM}; unless (defined $tag) { carp "Item called without tag"; return; } undef $$self{ITEM}; # Calculate the indentation and margin. $fits is set to true if the tag # will fit into the margin of the paragraph given our indentation level. my $indent = $$self{INDENTS}[-1]; $indent = $$self{opt_indent} unless defined $indent; my $margin = ' ' x $$self{opt_margin}; my $fits = ($$self{MARGIN} - $indent >= length ($tag) + 1); # If the tag doesn't fit, or if we have no associated text, print out the # tag separately. Otherwise, put the tag in the margin of the paragraph. if (!$text || $text =~ /^\s+$/ || !$fits) { my $realindent = $$self{MARGIN}; $$self{MARGIN} = $indent; my $output = $self->reformat ($tag); $output =~ s/^$margin /$margin:/ if ($$self{opt_alt} && $indent > 0); $output =~ s/\n*$/\n/; # If the text is just whitespace, we have an empty item paragraph; # this can result from =over/=item/=back without any intermixed # paragraphs. Insert some whitespace to keep the =item from merging # into the next paragraph. $output .= "\n" if $text && $text =~ /^\s*$/; $self->output ($output); $$self{MARGIN} = $realindent; $self->output ($self->reformat ($text)) if ($text && $text =~ /\S/); } else { my $space = ' ' x $indent; $space =~ s/^$margin /$margin:/ if $$self{opt_alt}; $text = $self->reformat ($text); $text =~ s/^$margin /$margin:/ if ($$self{opt_alt} && $indent > 0); my $tagspace = ' ' x length $tag; $text =~ s/^($space)$tagspace/$1$tag/ or warn "Bizarre space in item"; $self->output ($text); }}# Handle a basic block of text. The only tricky thing here is that if there# is a pending item tag, we need to format this as an item paragraph.sub cmd_para { my ($self, $attrs, $text) = @_; $text =~ s/\s+$/\n/; if (defined $$self{ITEM}) { $self->item ($text . "\n"); } else { $self->output ($self->reformat ($text . "\n")); } return '';}# Handle a verbatim paragraph. Just print it out, but indent it according to# our margin.sub cmd_verbatim { my ($self, $attrs, $text) = @_; $self->item if defined $$self{ITEM}; return if $text =~ /^\s*$/; $text =~ s/^(\n*)(\s*\S+)/$1 . (' ' x $$self{MARGIN}) . $2/gme; $text =~ s/\s*$/\n\n/; $self->output ($text); return '';}# Handle literal text (produced by =for and similar constructs). Just output# it with the minimum of changes.sub cmd_data { my ($self, $attrs, $text) = @_; $text =~ s/^\n+//; $text =~ s/\n{0,2}$/\n/; $self->output ($text); return '';}############################################################################### Headings############################################################################### The common code for handling all headers. Takes the header text, the# indentation, and the surrounding marker for the alt formatting method.sub heading { my ($self, $text, $indent, $marker) = @_; $self->item ("\n\n") if defined $$self{ITEM}; $text =~ s/\s+$//; if ($$self{opt_alt}) { my $closemark = reverse (split (//, $marker)); my $margin = ' ' x $$self{opt_margin}; $self->output ("\n" . "$margin$marker $text $closemark" . "\n\n"); } else { $text .= "\n" if $$self{opt_loose}; my $margin = ' ' x ($$self{opt_margin} + $indent); $self->output ($margin . $text . "\n"); } return '';}# First level heading.sub cmd_head1 { my ($self, $attrs, $text) = @_; $self->heading ($text, 0, '====');}# Second level heading.sub cmd_head2 {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -