App-DocKnot
view release on metacpan or search on metacpan
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
}
# Return the results.
return $want_format ? ($format, $text, @args) : ($text, @args);
}
# Expand a macro invocation.
#
# $definition - Definition of the macro
# $block - True if currently in block context
# @args - The arguments to the macro
#
# Returns: List with the macro expansion and the block context flag
sub _macro {
my ($self, $definition, $block, @args) = @_;
# The function that expands a macro substitution marker. If the number of
# the marker is higher than the number of arguments of the macro, leave it
# as-is. (We will have already warned about this when defining the
# macro.)
my $expand = sub {
my ($n) = @_;
return ($n > scalar(@args)) ? "\\\\$n" : $args[$n - 1];
};
# Replace the substitution markers in the macro definition.
$definition =~ s{ \\(\d+) }{ $expand->($1) }xmsge;
# Now parse the result as if it were input thread and return the results.
return $self->_parse_context($definition, $block);
}
# Expand a given command into its representation. This function is mutually
# recursive with _parse_context and _macro.
#
# $command - Name of the command
# $text - Input text following the command
# $block - True if currently in block context (if so, and if the command
# doesn't generate its own container, it will need to be wrapped
# in <p>
#
# Returns: List with the following elements:
# $output - Output from expanding the command
# $block - Whether the output is block context
# $text - Remaining unparsed text
sub _expand {
my ($self, $command, $text, $block) = @_;
# Special handling for expanding variables. These references look like
# \=NAME and expand to the value of the variable "NAME".
if ($command =~ m{ \A = \w }xms) {
my $variable = substr($command, 1);
if (exists($self->{variable}{$variable})) {
return ($self->{variable}{$variable}, 0, $text);
} else {
$self->_warning("unknown variable \\=$variable");
return (q{}, 0, $text);
}
}
# Special handling for macros. Macros shadow commands of the same name.
if (exists($self->{macro}{$command})) {
my ($args, $definition) = $self->{macro}{$command}->@*;
# Extract the macro arguments, if any were requested.
my @args;
if ($args != 0) {
($text, @args) = $self->_extract($text, $args, 0);
}
# The macro runs in a block context if we're currently in block
# context and there is no remaining non-whitespace text. Otherwise,
# use an inline context.
$block &&= $text =~ m{ \A \s* \z }xms;
# Expand the macro.
my ($result, $blocktag) = $self->_macro($definition, $block, @args);
# We have now double-counted all of the lines in the macro body
# itself, so we need to subtract the line count in the macro
# definition from the line number.
#
# This unfortunately means that the line number of errors that happen
# inside macro arguments will be somewhat off if the macro definition
# itself contains newlines. I don't see a way to avoid that without
# much more complex parsing and state tracking.
$self->{input}[-1][2] -= $definition =~ tr{\n}{};
# Return the macro results.
return ($result, $blocktag, $text);
}
# The normal command-handling case. Ensure it is a valid command.
if (!ref($COMMANDS{$command})) {
$self->_warning("unknown command or macro \\$command");
return (q{}, 1, $text);
}
# Dispatch the command to its handler.
my ($args, $handler, $want_format) = $COMMANDS{$command}->@*;
if ($want_format) {
my ($format, $rest, @args) = $self->_extract($text, $args, 1);
my ($blocktag, $output) = $self->$handler($format, @args);
return ($output, $blocktag, $rest);
} else {
my ($rest, @args) = $self->_extract($text, $args);
my ($blocktag, $output) = $self->$handler(@args);
return ($output, $blocktag, $rest);
}
}
# This is the heart of the input parser. Take a string of raw input, expand
# the commands in it, and format the results as HTML. This function is
# mutually recursive with _expand and _macro.
#
# This function is responsible for maintaining the line number in the file
# currently being processed, for error reporting. The strategy used is to
# increment the line number whenever a newline is seen in processed text.
# This means that newlines are not seen until the text containing them is
# parsed, which in turn means that every argument that may contain a newline
# must be parsed or must update the line number.
lib/App/DocKnot/Spin/Thread.pm view on Meta::CPAN
Here are the other inline commands:
=over 4
=item \break
A forced line break, C<< <br> >> in HTML.
=item \class[TEXT]
Does nothing except wrap TEXT in an HTML C<< <span> >> tag. The only purpose
of this command is to use it with a formatting instruction to generate an HTML
C<class> attribute on the C<< <span> >> tag. For example, you might write:
\class(red)[A style sheet can make this text red.]
and then use a style sheet that changes the text color for class C<red>.
=item \entity[CODE]
An HTML entity with code CODE. This normally becomes C<&CODE;> or C<&#CODE;>
in the generated HTML, depending on whether CODE is entirely numeric.
Use C<\entity[91]> and C<\entity[93]> for unbalanced C<[> and C<]> characters,
respectively.
Thread source is UTF-8, so this command is normally only necessary to escape
unbalanced square brackets.
=item \image[URL][TEXT]
Insert an inline image. TEXT is the alt text for the image (which will be
displayed on non-graphical browsers). Height and width tags are added
automatically if the URL is a relative path name and the corresponding file
exists and is supported by the Perl module Image::Size.
=item \link[URL][TEXT]
Create a link to URL with link text TEXT. Equivalent to C<< <a href> >>.
=item \release[PACKAGE]
If the C<versions> argument was provided, replaced with the latest release
date of PACKAGE. The date will be in the UTC time zone, not the local time
zone.
=item \size[FILE]
Replaced with the size of FILE in B, KB, MB, GB, or TB as is most appropriate,
without decimal places. The next largest unit is used if the value is larger
than 1024. 1024 is used as the scaling factor, not 1000.
=item \version[PACKAGE]
If the C<versions> argument was provided, replaced with the latest version of
PACKAGE.
=back
=head2 Defining Variables and Macros
One of the reasons to use thread instead of HTML is the ability to define new
macros on the fly. If there are constructs that are used more than once in
the page, you can define a macro at the top of that page and then use it
throughout the page.
A variable can be defined with the command:
\=[VARIABLE][VALUE]
where VARIABLE is the name that will be used (can only be alphanumerics plus
underscore) and VALUE is the value that string will expand into. Any later
occurrence of \=VARIABLE in the file will be replaced with <value>. For
example:
\=[FOO][some string]
will cause any later occurrences of C<\=FOO> in the file to be replaced with
the text C<some string>. Consider using this to collect external URLs for
links at the top of a page for easy updating.
A macro can be defined with the command:
\==[NAME][NARGS][DEFINITION]
where NAME is the name of the macro (again consisting only of alphanumerics or
underscore), NARGS is the number of arguments that it takes, and DEFINITION is
the definition of the macro.
When the macro is expanded, any occurrence of C<\1> in the definition is
replaced with the first argument, any occurrence of C<\2> with the second
argument, and so forth, and then the definition with those substitutions is
parsed as thread, as if it were written directly in the source page.
For example:
\==[bolddesc] [2] [\desc[\bold[\1]][\2]]
defines a macro C<\bolddesc> that takes the same arguments as the regular
C<\desc> command but always wraps the first argument, the heading, in C<<
<strong> >>.
=head1 AUTHOR
Russ Allbery <rra@cpan.org>
=head1 COPYRIGHT AND LICENSE
Copyright 1999-2011, 2013, 2021-2023 Russ Allbery <rra@cpan.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
( run in 1.143 second using v1.01-cache-2.11-cpan-13bb782fe5a )