App-DocKnot
view release on metacpan or search on metacpan
lib/App/DocKnot/Generate.pm view on Meta::CPAN
# Build the initial notice with the word copyright and the years.
my $text = 'Copyright ' . $copyright->{years};
local $Text::Wrap::columns = $self->{width} + 1;
local $Text::Wrap::unexpand = 0;
$text = wrap($prefix, $prefix . q{ } x 4, $text);
# See if the holder fits on the last line. If so, add it there;
# otherwise, add another line.
my $last_length;
if (rindex($text, "\n") == -1) {
$last_length = length($text);
} else {
$last_length = length($text) - rindex($text, "\n");
}
if ($last_length + length($holder) < $self->{width}) {
$text .= " $holder";
} else {
$text .= "\n" . $prefix . q{ } x 4 . $holder;
}
$notice .= $text . "\n";
}
chomp($notice);
return $notice;
};
return $copyright;
}
# Returns code to indent each line of a paragraph by a given number of spaces.
# This is constructed as a method returning a closure so that its behavior can
# be influenced by App::DocKnot configuration in the future, but it currently
# doesn't use any configuration. It takes the indentation and an optional
# prefix to put at the start of each line.
#
# Returns: Code reference to a closure
sub _code_for_indent {
my ($self) = @_;
my $indent = sub {
my ($text, $space, $lead) = @_;
$lead //= q{};
my @text = split(m{\n}xms, $text);
return join("\n", map { $lead . q{ } x $space . $_ } @text);
};
return $indent;
}
# Returns code that converts metadata text (which is assumed to be in
# Markdown) to text. This is not a complete Markdown formatter. It only
# supports the bits of markup that I've had some reason to use.
#
# This is constructed as a method returning a closure so that its behavior can
# be influenced by App::DocKnot configuration in the future, but it currently
# doesn't use any configuration.
#
# Returns: Code reference to a closure that takes a block of text and returns
# the converted text
sub _code_for_to_text {
my ($self) = @_;
my $to_text = sub {
my ($text) = @_;
# Remove triple backticks but escape all backticks inside them.
$text =~ s{ ``` \w* (\s .*?) ``` }{
my $text = $1;
$text =~ s{ [\`] }{``}xmsg;
$text;
}xmsge;
# Remove backticks, but don't look at things starting with doubled
# backticks.
$text =~ s{ (?<! \` ) ` ([^\`]+) ` }{$1}xmsg;
# Undo backtick escaping.
$text =~ s{ `` }{\`}xmsg;
# Rewrite quoted paragraphs to have four spaces of additional
# indentation.
$text =~ s{
\n \n # start of paragraph
( # start of the text
(> \s+) # quote mark on first line
\S [^\n]* \n # first line
(?: # all subsequent lines
\2 \S [^\n]* \n # start with the same prefix
)* # any number of subsequent lines
) # end of the text
}{
my ($text, $prefix) = ($1, $2);
$text =~ s{ ^ \Q$prefix\E }{ }xmsg;
"\n\n" . $text;
}xmsge;
# For each paragraph, remove URLs from all links, replacing them with
# numeric references, and accumulate the mapping of numbers to URLs in
# %urls. Then, add to the end of the paragraph the references and
# URLs.
my $ref = 1;
my @paragraphs = split(m{ \n\n }xms, $text);
for my $para (@paragraphs) {
my %urls;
my $regex = qr{ \[([^\]]+)\] [(] (\S+) [)] }xms;
while ($para =~ s{$regex}{$1 [$ref]}xms) {
$urls{$ref} = $2;
$ref++;
}
if (%urls) {
my @refs = map { "[$_] $urls{$_}" } sort { $a <=> $b }
keys(%urls);
$para .= "\n\n" . join("\n", q{}, @refs, q{});
}
}
# Rejoin the paragraphs and return the result.
return join("\n\n", @paragraphs);
};
return $to_text;
}
# Returns code that converts metadata text (which is assumed to be in
# Markdown) to thread. This is not a complete Markdown formatter. It only
# supports the bits of markup that I've had some reason to use.
#
# This is constructed as a method returning a closure so that its behavior can
# be influenced by App::DocKnot configuration in the future, but it currently
# doesn't use any configuration.
#
# Returns: Code reference to a closure that takes a block of text and returns
# the converted thread
sub _code_for_to_thread {
my ($self) = @_;
my $to_thread = sub {
my ($text) = @_;
# Escape all backslashes.
$text =~ s{ \\ }{\\\\}xmsg;
# Rewrite triple backticks to \pre blocks and escape backticks inside
# them so that they're not turned into \code blocks.
$text =~ s{ ``` \w* (\s .*?) ``` }{
my $text = $1;
$text =~ s{ [\`] }{``}xmsg;
'\pre[' . $1 . ']';
}xmsge;
# Rewrite backticks to \code blocks.
$text =~ s{ ` ([^\`]+) ` }{\\code[$1]}xmsg;
# Undo backtick escaping.
$text =~ s{ `` }{\`}xmsg;
# Rewrite all Markdown links into thread syntax.
$text =~ s{ \[ ([^\]]+) \] [(] (\S+) [)] }{\\link[$2][$1]}xmsg;
# Rewrite long bullets. This is quite tricky since we have to grab
# every line from the first bulleted one to the point where the
# indentation stops.
$text =~ s{
( # capture whole contents
^ (\s*) # indent before bullet
[*] (\s+) # bullet and following indent
[^\n]+ \n # rest of line
(?: \s* \n )* # optional blank lines
(\2 [ ] \3) # matching indent
[^\n]+ \n # rest of line
(?: # one or more of
\4 # matching indent
[^\n]+ \n # rest of line
| # or
\s* \n # blank lines
)+ # end of indented block
) # full bullet with leading bullet
}{
my $text = $1;
$text =~ s{ [*] }{ }xms;
"\\bullet[\n\n" . $text . "\n]\n";
}xmsge;
# Do the same thing, but with numbered lists. This doesn't handle
# numbers larger than 9 currently, since that requires massaging the
# spacing.
$text =~ s{
( # capture whole contents
^ (\s*) # indent before number
\d [.] (\s+) # number and following indent
[^\n]+ \n # rest of line
(?: \s* \n )* # optional blank lines
(\2 [ ][ ] \3) # matching indent
[^\n]+ \n # rest of line
(?: # one or more of
\4 # matching indent
[^\n]+ \n # rest of line
| # or
\s* \n # blank lines
)+ # end of indented block
) # full bullet with leading bullet
}{
my $text = $1;
( run in 0.741 second using v1.01-cache-2.11-cpan-df04353d9ac )