From 9ba8e6cf38da5196ed7bc878fe452952f3e10638 Mon Sep 17 00:00:00 2001
From: Magnus Auvinen
Date: Tue, 22 May 2007 15:06:55 +0000
Subject: moved docs
---
docs/doctool/Modules/NaturalDocs/Parser/Native.pm | 926 +++++++++++++++++++++
.../Modules/NaturalDocs/Parser/ParsedTopic.pm | 210 +++++
2 files changed, 1136 insertions(+)
create mode 100644 docs/doctool/Modules/NaturalDocs/Parser/Native.pm
create mode 100644 docs/doctool/Modules/NaturalDocs/Parser/ParsedTopic.pm
(limited to 'docs/doctool/Modules/NaturalDocs/Parser')
diff --git a/docs/doctool/Modules/NaturalDocs/Parser/Native.pm b/docs/doctool/Modules/NaturalDocs/Parser/Native.pm
new file mode 100644
index 00000000..b88c7bac
--- /dev/null
+++ b/docs/doctool/Modules/NaturalDocs/Parser/Native.pm
@@ -0,0 +1,926 @@
+###############################################################################
+#
+# Package: NaturalDocs::Parser::Native
+#
+###############################################################################
+#
+# A package that converts comments from Natural Docs' native format into objects.
+# Unlike most second-level packages, these are packages and not object classes.
+#
+###############################################################################
+
+# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure
+# Natural Docs is licensed under the GPL
+
+
+use strict;
+use integer;
+
+package NaturalDocs::Parser::Native;
+
+
+###############################################################################
+# Group: Variables
+
+
+# Return values of TagType(). Not documented here.
+use constant POSSIBLE_OPENING_TAG => 1;
+use constant POSSIBLE_CLOSING_TAG => 2;
+use constant NOT_A_TAG => 3;
+
+
+#
+# var: package
+#
+# A representing the package normal topics will be a part of at the current point in the file. This is a package variable
+# because it needs to be reserved between function calls.
+#
+my $package;
+
+#
+# hash: functionListIgnoredHeadings
+#
+# An existence hash of all the headings that prevent the parser from creating function list symbols. Whenever one of
+# these headings are used in a function list topic, symbols are not created from definition lists until the next heading. The keys
+# are in all lowercase.
+#
+my %functionListIgnoredHeadings = ( 'parameters' => 1,
+ 'parameter' => 1,
+ 'params' => 1,
+ 'param' => 1,
+ 'arguments' => 1,
+ 'argument' => 1,
+ 'args' => 1,
+ 'arg' => 1 );
+
+
+###############################################################################
+# Group: Interface Functions
+
+
+#
+# Function: Start
+#
+# This will be called whenever a file is about to be parsed. It allows the package to reset its internal state.
+#
+sub Start
+ {
+ my ($self) = @_;
+ $package = undef;
+ };
+
+
+#
+# Function: ParseComment
+#
+# This will be called whenever a comment capable of containing Natural Docs content is found.
+#
+# Parameters:
+#
+# commentLines - An arrayref of the comment's lines. All tabs must be expanded into spaces, and all comment symbols,
+# boxes, lines, and trailing whitespace should be removed. Leading whitespace and multiple blank lines
+# should be preserved.
+# lineNumber - The line number of the first of the comment lines.
+# parsedTopics - A reference to the array where any new should be placed.
+#
+# Returns:
+#
+# The number of parsed topics added to the array, or zero if none.
+#
+sub ParseComment #(commentLines, lineNumber, parsedTopics)
+ {
+ my ($self, $commentLines, $lineNumber, $parsedTopics) = @_;
+
+ my $topicCount = 0;
+ my $prevLineBlank = 1;
+ my $inCodeSection;
+
+ my $type;
+ my $typeInfo;
+ my $isPlural;
+ my $title;
+ my $symbol;
+ #my $package; # package variable.
+
+ my $index = 0;
+
+ my $bodyStart = 0;
+ my $bodyEnd = 0; # Not inclusive.
+
+ while ($index < scalar @$commentLines)
+ {
+ # Everything but leading whitespace was removed beforehand.
+
+ # If we're in a code section...
+ if ($inCodeSection)
+ {
+ if ($commentLines->[$index] =~ /^ *\( *(?:end|finish|done)(?: +(?:table|code|example|diagram))? *\)$/i)
+ { $inCodeSection = undef; };
+
+ $prevLineBlank = 0;
+ $bodyEnd++;
+ }
+
+ # If the line is empty...
+ elsif (!length($commentLines->[$index]))
+ {
+ $prevLineBlank = 1;
+ $bodyEnd++;
+ }
+
+ # If the line has a recognized header and the previous line is blank...
+ elsif ($prevLineBlank &&
+ $commentLines->[$index] =~ /^ *([a-z0-9 ]*[a-z0-9]): +(.*)$/i &&
+ (my ($newType, $newTypeInfo, $newIsPlural) = NaturalDocs::Topics->KeywordInfo($1)) )
+ {
+ my $newTitle = $2;
+
+ # Process the previous one, if any.
+
+ if (defined $type)
+ {
+ if ($typeInfo->Scope() == ::SCOPE_START() || $typeInfo->Scope() == ::SCOPE_END())
+ { $package = undef; };
+
+ my $body = $self->FormatBody($commentLines, $bodyStart, $bodyEnd, $type, $isPlural);
+ my $newTopic = $self->MakeParsedTopic($type, $title, $package, $body, $lineNumber + $bodyStart - 1, $isPlural);
+ push @$parsedTopics, $newTopic;
+ $topicCount++;
+
+ $package = $newTopic->Package();
+ };
+
+ ($type, $typeInfo, $isPlural, $title) = ($newType, $newTypeInfo, $newIsPlural, $newTitle);
+
+ $bodyStart = $index + 1;
+ $bodyEnd = $index + 1;
+
+ $prevLineBlank = 0;
+ }
+
+ # Line without recognized header
+ else
+ {
+ $prevLineBlank = 0;
+ $bodyEnd++;
+
+ if ($commentLines->[$index] =~ /^ *\( *(?:(?:start|begin)? +)?(?:table|code|example|diagram) *\)$/i)
+ { $inCodeSection = 1; };
+ };
+
+
+ $index++;
+ };
+
+
+ # Last one, if any. This is the only one that gets the prototypes.
+ if (defined $type)
+ {
+ if ($typeInfo->Scope() == ::SCOPE_START() || $typeInfo->Scope() == ::SCOPE_END())
+ { $package = undef; };
+
+ my $body = $self->FormatBody($commentLines, $bodyStart, $bodyEnd, $type, $isPlural);
+ my $newTopic = $self->MakeParsedTopic($type, $title, $package, $body, $lineNumber + $bodyStart - 1, $isPlural);
+ push @$parsedTopics, $newTopic;
+ $topicCount++;
+
+ $package = $newTopic->Package();
+ };
+
+ return $topicCount;
+ };
+
+
+
+###############################################################################
+# Group: Support Functions
+
+
+#
+# Function: MakeParsedTopic
+#
+# Creates a object for the passed parameters. Scope is gotten from
+# the package variable instead of from the parameters. The summary is generated from the body.
+#
+# Parameters:
+#
+# type - The .
+# title - The title of the topic.
+# package - The package the topic appears in.
+# body - The topic's body in .
+# lineNumber - The topic's line number.
+# isList - Whether the topic is a list.
+#
+# Returns:
+#
+# The object.
+#
+sub MakeParsedTopic #(type, title, package, body, lineNumber, isList)
+ {
+ my ($self, $type, $title, $package, $body, $lineNumber, $isList) = @_;
+
+ my $summary;
+
+ if (defined $body)
+ { $summary = NaturalDocs::Parser->GetSummaryFromBody($body); };
+
+ return NaturalDocs::Parser::ParsedTopic->New($type, $title, $package, undef, undef, $summary,
+ $body, $lineNumber, $isList);
+ };
+
+
+#
+# Function: FormatBody
+#
+# Converts the section body to .
+#
+# Parameters:
+#
+# commentLines - The arrayref of comment lines.
+# startingIndex - The starting index of the body to format.
+# endingIndex - The ending index of the body to format, *not* inclusive.
+# type - The type of the section.
+# isList - Whether it's a list topic.
+#
+# Returns:
+#
+# The body formatted in .
+#
+sub FormatBody #(commentLines, startingIndex, endingIndex, type, isList)
+ {
+ my ($self, $commentLines, $startingIndex, $endingIndex, $type, $isList) = @_;
+
+ use constant TAG_NONE => 1;
+ use constant TAG_PARAGRAPH => 2;
+ use constant TAG_BULLETLIST => 3;
+ use constant TAG_DESCRIPTIONLIST => 4;
+ use constant TAG_HEADING => 5;
+ use constant TAG_PREFIXCODE => 6;
+ use constant TAG_TAGCODE => 7;
+
+ my %tagEnders = ( TAG_NONE() => '',
+ TAG_PARAGRAPH() => '
',
+ TAG_BULLETLIST() => '',
+ TAG_DESCRIPTIONLIST() => '',
+ TAG_HEADING() => '',
+ TAG_PREFIXCODE() => '',
+ TAG_TAGCODE() => '' );
+
+ my $topLevelTag = TAG_NONE;
+
+ my $output;
+ my $textBlock;
+ my $prevLineBlank = 1;
+
+ my $codeBlock;
+ my $removedCodeSpaces;
+
+ my $ignoreListSymbols;
+
+ my $index = $startingIndex;
+
+ while ($index < $endingIndex)
+ {
+ # If we're in a tagged code section...
+ if ($topLevelTag == TAG_TAGCODE)
+ {
+ if ($commentLines->[$index] =~ /^ *\( *(?:end|finish|done)(?: +(?:table|code|example|diagram))? *\)$/i)
+ {
+ $codeBlock =~ s/\n+$//;
+ $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '';
+ $codeBlock = undef;
+ $topLevelTag = TAG_NONE;
+ $prevLineBlank = undef;
+ }
+ else
+ {
+ $self->AddToCodeBlock($commentLines->[$index], \$codeBlock, \$removedCodeSpaces);
+ };
+ }
+
+ # If the line starts with a code designator...
+ elsif ($commentLines->[$index] =~ /^ *[>:|](.*)$/)
+ {
+ my $code = $1;
+
+ if ($topLevelTag == TAG_PREFIXCODE)
+ {
+ $self->AddToCodeBlock($code, \$codeBlock, \$removedCodeSpaces);
+ }
+ else # $topLevelTag != TAG_PREFIXCODE
+ {
+ if (defined $textBlock)
+ {
+ $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag};
+ $textBlock = undef;
+ };
+
+ $topLevelTag = TAG_PREFIXCODE;
+ $output .= '';
+ $self->AddToCodeBlock($code, \$codeBlock, \$removedCodeSpaces);
+ };
+ }
+
+ # If we're not in either code style...
+ else
+ {
+ # Strip any leading whitespace.
+ $commentLines->[$index] =~ s/^ +//;
+
+ # If we were in a prefixed code section...
+ if ($topLevelTag == TAG_PREFIXCODE)
+ {
+ $codeBlock =~ s/\n+$//;
+ $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '';
+ $codeBlock = undef;
+ $topLevelTag = TAG_NONE;
+ $prevLineBlank = undef;
+ };
+
+
+ # If the line is blank...
+ if (!length($commentLines->[$index]))
+ {
+ # End a paragraph. Everything else ignores it for now.
+ if ($topLevelTag == TAG_PARAGRAPH)
+ {
+ $output .= $self->RichFormatTextBlock($textBlock) . '';
+ $textBlock = undef;
+ $topLevelTag = TAG_NONE;
+ };
+
+ $prevLineBlank = 1;
+ }
+
+ # If the line starts with a bullet...
+ elsif ($commentLines->[$index] =~ /^[-\*o+] +([^ ].*)$/)
+ {
+ my $bulletedText = $1;
+
+ if (defined $textBlock)
+ { $output .= $self->RichFormatTextBlock($textBlock); };
+
+ if ($topLevelTag == TAG_BULLETLIST)
+ {
+ $output .= '
';
+ };
+
+ return $output;
+ };
+
+
+#
+# Function: AddToCodeBlock
+#
+# Adds a line of text to a code block, handling all the indentation processing required.
+#
+# Parameters:
+#
+# line - The line of text to add.
+# codeBlockRef - A reference to the code block to add it to.
+# removedSpacesRef - A reference to a variable to hold the number of spaces removed. It needs to be stored between calls.
+# It will reset itself automatically when the code block codeBlockRef points to is undef.
+#
+sub AddToCodeBlock #(line, codeBlockRef, removedSpacesRef)
+ {
+ my ($self, $line, $codeBlockRef, $removedSpacesRef) = @_;
+
+ $line =~ /^( *)(.*)$/;
+ my ($spaces, $code) = ($1, $2);
+
+ if (!defined $$codeBlockRef)
+ {
+ if (length($code))
+ {
+ $$codeBlockRef = $code . "\n";
+ $$removedSpacesRef = length($spaces);
+ };
+ # else ignore leading line breaks.
+ }
+
+ elsif (length $code)
+ {
+ # Make sure we have the minimum amount of spaces to the left possible.
+ if (length($spaces) != $$removedSpacesRef)
+ {
+ my $spaceDifference = abs( length($spaces) - $$removedSpacesRef );
+ my $spacesToAdd = ' ' x $spaceDifference;
+
+ if (length($spaces) > $$removedSpacesRef)
+ {
+ $$codeBlockRef .= $spacesToAdd;
+ }
+ else
+ {
+ $$codeBlockRef =~ s/^(.)/$spacesToAdd . $1/gme;
+ $$removedSpacesRef = length($spaces);
+ };
+ };
+
+ $$codeBlockRef .= $code . "\n";
+ }
+
+ else # (!length $code)
+ {
+ $$codeBlockRef .= "\n";
+ };
+ };
+
+
+#
+# Function: RichFormatTextBlock
+#
+# Applies rich formatting to a chunk of text. This includes both amp chars, formatting tags, and link tags.
+#
+# Parameters:
+#
+# text - The block of text to format.
+#
+# Returns:
+#
+# The formatted text block.
+#
+sub RichFormatTextBlock #(text)
+ {
+ my ($self, $text) = @_;
+ my $output;
+
+
+ # Split the text from the potential tags.
+
+ my @tempTextBlocks = split(/([\*_<>])/, $text);
+
+ # Since the symbols are considered dividers, empty strings could appear between two in a row or at the beginning/end of the
+ # array. This could seriously screw up TagType(), so we need to get rid of them.
+ my @textBlocks;
+
+ while (scalar @tempTextBlocks)
+ {
+ my $tempTextBlock = shift @tempTextBlocks;
+
+ if (length $tempTextBlock)
+ { push @textBlocks, $tempTextBlock; };
+ };
+
+
+ my $bold;
+ my $underline;
+ my $underlineHasWhitespace;
+
+ my $index = 0;
+
+ while ($index < scalar @textBlocks)
+ {
+ if ($textBlocks[$index] eq '<' && $self->TagType(\@textBlocks, $index) == POSSIBLE_OPENING_TAG)
+ {
+ my $endingIndex = $self->ClosingTag(\@textBlocks, $index, undef);
+
+ if ($endingIndex != -1)
+ {
+ my $linkText;
+ $index++;
+
+ while ($index < $endingIndex)
+ {
+ $linkText .= $textBlocks[$index];
+ $index++;
+ };
+ # Index will be incremented again at the end of the loop.
+
+ if ($linkText =~ /^(?:mailto\:)?((?:[a-z0-9\-_]+\.)*[a-z0-9\-_]+@(?:[a-z0-9\-]+\.)+[a-z]{2,4})$/i)
+ { $output .= '' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . ''; }
+ elsif ($linkText =~ /^(?:http|https|ftp|news|file)\:/i)
+ { $output .= '' . NaturalDocs::NDMarkup->ConvertAmpChars($linkText) . ''; }
+ else
+ { $output .= '' . NaturalDocs::NDMarkup->ConvertAmpChars($linkText) . ''; };
+ }
+
+ else # it's not a link.
+ {
+ $output .= '<';
+ };
+ }
+
+ elsif ($textBlocks[$index] eq '*')
+ {
+ my $tagType = $self->TagType(\@textBlocks, $index);
+
+ if ($tagType == POSSIBLE_OPENING_TAG && $self->ClosingTag(\@textBlocks, $index, undef) != -1)
+ {
+ # ClosingTag() makes sure tags aren't opened multiple times in a row.
+ $bold = 1;
+ $output .= '';
+ }
+ elsif ($bold && $tagType == POSSIBLE_CLOSING_TAG)
+ {
+ $bold = undef;
+ $output .= '';
+ }
+ else
+ {
+ $output .= '*';
+ };
+ }
+
+ elsif ($textBlocks[$index] eq '_')
+ {
+ my $tagType = $self->TagType(\@textBlocks, $index);
+
+ if ($tagType == POSSIBLE_OPENING_TAG && $self->ClosingTag(\@textBlocks, $index, \$underlineHasWhitespace) != -1)
+ {
+ # ClosingTag() makes sure tags aren't opened multiple times in a row.
+ $underline = 1;
+ #underlineHasWhitespace is set by ClosingTag().
+ $output .= '';
+ }
+ elsif ($underline && $tagType == POSSIBLE_CLOSING_TAG)
+ {
+ $underline = undef;
+ #underlineHasWhitespace will be reset by the next opening underline.
+ $output .= '';
+ }
+ elsif ($underline && !$underlineHasWhitespace)
+ {
+ # If there's no whitespace between underline tags, all underscores are replaced by spaces so
+ # _some_underlined_text_ becomes some underlined text. The standard _some underlined text_
+ # will work too.
+ $output .= ' ';
+ }
+ else
+ {
+ $output .= '_';
+ };
+ }
+
+ else # plain text or a > that isn't part of a link
+ {
+ $output .= NaturalDocs::NDMarkup->ConvertAmpChars($textBlocks[$index]);;
+ };
+
+ $index++;
+ };
+
+
+ # Pull out the e-mail addresses and URLs that aren't in angle brackets. Preventing > from being the leading character will
+ # prevent it from duplicating and tags, although I don't know if it may be falsely triggered in other situations
+ # as well.
+
+ $output =~ s{
+ # The previous character can't be an alphanumeric.
+ (?] )
+
+ # Optional mailto:. Ignored in output.
+ (?:mailto\:)?
+
+ # Begin capture
+ (
+
+ # The user portion. Alphanumeric and - _. Dots can appear between, but not at the edges or more than
+ # one in a row.
+ (?: [a-z0-9\-_]+ \. )* [a-z0-9\-_]+
+
+ @
+
+ # The domain. Alphanumeric and -. Dots same as above, however, there must be at least two sections
+ # and the last one must be two to four alphanumeric characters (.com, .uk, .info, .203 for IP addresses)
+ (?: [a-z0-9\-]+ \. )+ [a-z]{2,4}
+
+ # End capture.
+ )
+
+ # The next character can't be an alphanumeric, which should prevent .abcde from matching the two to
+ # four character requirement.
+ (?! [a-z0-9] )
+
+ }
+
+ {$1<\/email>}igx;
+
+ $output =~ s{
+ # The previous character can't be an alphanumeric.
+ (?] )
+
+ # Begin capture.
+ (
+
+ # URL must start with one of the acceptable protocols.
+ (?:http|https|ftp|news|file)\:
+
+ # The acceptable URL characters as far as I know.
+ [a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?\.\,]*
+
+ # The URL characters minus period and comma. If it ends on them, they're probably intended as punctuation.
+ [a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?]
+
+ # End capture.
+ )
+
+ # The next character must not be an acceptable character. This will prevent the URL from ending early just
+ # to get a match.
+ (?! [a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?] )
+
+ }
+ {$1<\/url>}igx;
+
+ return $output;
+ };
+
+
+#
+# Function: TagType
+#
+# Returns whether the tag is a possible opening or closing tag, or neither. "Possible" because it doesn't check if an opening tag is
+# closed or a closing tag is opened, just whether the surrounding characters allow it to be a candidate for a tag. For example, in
+# "A _B" the underscore is a possible opening underline tag, but in "A_B" it is not. Support function for .
+#
+# Parameters:
+#
+# textBlocks - A reference to an array of text blocks.
+# index - The index of the tag.
+#
+# Returns:
+#
+# POSSIBLE_OPENING_TAG, POSSIBLE_CLOSING_TAG, or NOT_A_TAG.
+#
+sub TagType #(textBlocks, index)
+ {
+ my ($self, $textBlocks, $index) = @_;
+
+
+ # Possible opening tags
+
+ if ( ( $textBlocks->[$index] =~ /^[\*_<]$/ ) &&
+
+ # Before it must be whitespace, the beginning of the text, or ({["'-/.
+ ( $index == 0 || $textBlocks->[$index-1] =~ /[\ \t\n\(\{\[\"\'\-\/]$/ )&&
+
+ # After it must be non-whitespace.
+ ( $index + 1 < scalar @$textBlocks && $textBlocks->[$index+1] !~ /^[\ \t\n]/) &&
+
+ # Make sure we don't accept <<, <=, <-, or *= as opening tags
+ ( $textBlocks->[$index] ne '<' || $textBlocks->[$index+1] !~ /^[<=-]/ ) &&
+ ( $textBlocks->[$index] ne '*' || $textBlocks->[$index+1] !~ /^\=/ ) )
+ {
+ return POSSIBLE_OPENING_TAG;
+ }
+
+
+ # Possible closing tags
+
+ elsif ( ( $textBlocks->[$index] =~ /^[\*_>]$/) &&
+
+ # After it must be whitespace, the end of the text, or )}].,!?"';:-/.
+ ( $index + 1 == scalar @$textBlocks || $textBlocks->[$index+1] =~ /^[ \t\n\)\]\}\.\,\!\?\"\'\;\:\-\/]/ ||
+ # Links also get plurals, like s, es, 's, and '.
+ ( $textBlocks->[$index] eq '>' && $textBlocks->[$index+1] =~ /^(?:es|s|\')/ ) ) &&
+
+ # Before it must be non-whitespace.
+ ( $index != 0 && $textBlocks->[$index-1] !~ /[ \t\n]$/ ) &&
+
+ # Make sure we don't accept >>, ->, or => as closing tags. >= is already taken care of.
+ ( $textBlocks->[$index] ne '>' || $textBlocks->[$index-1] !~ /[>=-]$/ ) )
+ {
+ return POSSIBLE_CLOSING_TAG;
+ }
+
+ else
+ {
+ return NOT_A_TAG;
+ };
+
+ };
+
+
+#
+# Function: ClosingTag
+#
+# Returns whether a tag is closed or not, where it's closed if it is, and optionally whether there is any whitespace between the
+# tags. Support function for .
+#
+# The results of this function are in full context, meaning that if it says a tag is closed, it can be interpreted as that tag in the
+# final output. It takes into account any spoiling factors, like there being two opening tags in a row.
+#
+# Parameters:
+#
+# textBlocks - A reference to an array of text blocks.
+# index - The index of the opening tag.
+# hasWhitespaceRef - A reference to the variable that will hold whether there is whitespace between the tags or not. If
+# undef, the function will not check. If the tag is not closed, the variable will not be changed.
+#
+# Returns:
+#
+# If the tag is closed, it returns the index of the closing tag and puts whether there was whitespace between the tags in
+# hasWhitespaceRef if it was specified. If the tag is not closed, it returns -1 and doesn't touch the variable pointed to by
+# hasWhitespaceRef.
+#
+sub ClosingTag #(textBlocks, index, hasWhitespace)
+ {
+ my ($self, $textBlocks, $index, $hasWhitespaceRef) = @_;
+
+ my $hasWhitespace;
+ my $closingTag;
+
+ if ($textBlocks->[$index] eq '*' || $textBlocks->[$index] eq '_')
+ { $closingTag = $textBlocks->[$index]; }
+ elsif ($textBlocks->[$index] eq '<')
+ { $closingTag = '>'; }
+ else
+ { return -1; };
+
+ my $beginningIndex = $index;
+ $index++;
+
+ while ($index < scalar @$textBlocks)
+ {
+ if ($textBlocks->[$index] eq '<' && $self->TagType($textBlocks, $index) == POSSIBLE_OPENING_TAG)
+ {
+ # If we hit a < and we're checking whether a link is closed, it's not. The first < becomes literal and the second one
+ # becomes the new link opening.
+ if ($closingTag eq '>')
+ {
+ return -1;
+ }
+
+ # If we're not searching for the end of a link, we have to skip the link because formatting tags cannot appear within
+ # them. That's of course provided it's closed.
+ else
+ {
+ my $linkHasWhitespace;
+
+ my $endIndex = $self->ClosingTag($textBlocks, $index,
+ ($hasWhitespaceRef && !$hasWhitespace ? \$linkHasWhitespace : undef) );
+
+ if ($endIndex != -1)
+ {
+ if ($linkHasWhitespace)
+ { $hasWhitespace = 1; };
+
+ # index will be incremented again at the end of the loop, which will bring us past the link's >.
+ $index = $endIndex;
+ };
+ };
+ }
+
+ elsif ($textBlocks->[$index] eq $closingTag)
+ {
+ my $tagType = $self->TagType($textBlocks, $index);
+
+ if ($tagType == POSSIBLE_CLOSING_TAG)
+ {
+ # There needs to be something between the tags for them to count.
+ if ($index == $beginningIndex + 1)
+ { return -1; }
+ else
+ {
+ # Success!
+
+ if ($hasWhitespaceRef)
+ { $$hasWhitespaceRef = $hasWhitespace; };
+
+ return $index;
+ };
+ }
+
+ # If there are two opening tags of the same type, the first becomes literal and the next becomes part of a tag.
+ elsif ($tagType == POSSIBLE_OPENING_TAG)
+ { return -1; }
+ }
+
+ elsif ($hasWhitespaceRef && !$hasWhitespace)
+ {
+ if ($textBlocks->[$index] =~ /[ \t\n]/)
+ { $hasWhitespace = 1; };
+ };
+
+ $index++;
+ };
+
+ # Hit the end of the text blocks if we're here.
+ return -1;
+ };
+
+
+1;
diff --git a/docs/doctool/Modules/NaturalDocs/Parser/ParsedTopic.pm b/docs/doctool/Modules/NaturalDocs/Parser/ParsedTopic.pm
new file mode 100644
index 00000000..e978b985
--- /dev/null
+++ b/docs/doctool/Modules/NaturalDocs/Parser/ParsedTopic.pm
@@ -0,0 +1,210 @@
+###############################################################################
+#
+# Package: NaturalDocs::Parser::ParsedTopic
+#
+###############################################################################
+#
+# A class for parsed topics of source files. Also encompasses some of the -specific behavior.
+#
+###############################################################################
+
+# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure
+# Natural Docs is licensed under the GPL
+
+use strict;
+use integer;
+
+package NaturalDocs::Parser::ParsedTopic;
+
+
+###############################################################################
+# Group: Implementation
+
+#
+# Constants: Members
+#
+# The object is a blessed arrayref with the following indexes.
+#
+# TYPE - The .
+# TITLE - The title of the topic.
+# PACKAGE - The package the topic appears in, or undef if none.
+# USING - An arrayref of additional package available to the topic via "using" statements, or undef if
+# none.
+# PROTOTYPE - The prototype, if it exists and is applicable.
+# SUMMARY - The summary, if it exists.
+# BODY - The body of the topic, formatted in . Some topics may not have bodies, and if not, this
+# will be undef.
+# LINE_NUMBER - The line number the topic appears at in the file.
+# IS_LIST - Whether the topic is a list.
+#
+use NaturalDocs::DefineMembers 'TYPE', 'TITLE', 'PACKAGE', 'USING', 'PROTOTYPE', 'SUMMARY', 'BODY',
+ 'LINE_NUMBER', 'IS_LIST';
+# DEPENDENCY: New() depends on the order of these constants, and that this class is not inheriting any members.
+
+
+###############################################################################
+# Group: Functions
+
+#
+# Function: New
+#
+# Creates a new object.
+#
+# Parameters:
+#
+# type - The .
+# title - The title of the topic.
+# package - The package the topic appears in, or undef if none.
+# using - An arrayref of additional package available to the topic via "using" statements, or undef if none.
+# prototype - The prototype, if it exists and is applicable. Otherwise set to undef.
+# summary - The summary of the topic, if any.
+# body - The body of the topic, formatted in . May be undef, as some topics may not have bodies.
+# lineNumber - The line number the topic appears at in the file.
+# isList - Whether the topic is a list topic or not.
+#
+# Returns:
+#
+# The new object.
+#
+sub New #(type, title, package, using, prototype, summary, body, lineNumber, isList)
+ {
+ # DEPENDENCY: This depends on the order of the parameter list being the same as the constants, and that there are no
+ # members inherited from a base class.
+
+ my $package = shift;
+
+ my $object = [ @_ ];
+ bless $object, $package;
+
+ if (defined $object->[USING])
+ { $object->[USING] = [ @{$object->[USING]} ]; };
+
+ return $object;
+ };
+
+
+# Function: Type
+# Returns the .
+sub Type
+ { return $_[0]->[TYPE]; };
+
+# Function: SetType
+# Replaces the .
+sub SetType #(type)
+ { $_[0]->[TYPE] = $_[1]; };
+
+# Function: IsList
+# Returns whether the topic is a list.
+sub IsList
+ { return $_[0]->[IS_LIST]; };
+
+# Function: SetIsList
+# Sets whether the topic is a list.
+sub SetIsList
+ { $_[0]->[IS_LIST] = $_[1]; };
+
+# Function: Title
+# Returns the title of the topic.
+sub Title
+ { return $_[0]->[TITLE]; };
+
+#
+# Function: Symbol
+#
+# Returns the defined by the topic. It is fully resolved and does _not_ need to be joined with .
+#
+# Type-Specific Behavior:
+#
+# - If the is always global, the symbol will be generated from the title only.
+# - Everything else's symbols will be generated from the title and the package passed to .
+#
+sub Symbol
+ {
+ my ($self) = @_;
+
+ my $titleSymbol = NaturalDocs::SymbolString->FromText($self->[TITLE]);
+
+ if (NaturalDocs::Topics->TypeInfo($self->Type())->Scope() == ::SCOPE_ALWAYS_GLOBAL())
+ { return $titleSymbol; }
+ else
+ {
+ return NaturalDocs::SymbolString->Join( $self->[PACKAGE], $titleSymbol );
+ };
+ };
+
+
+#
+# Function: Package
+#
+# Returns the package that the topic appears in.
+#
+# Type-Specific Behavior:
+#
+# - If the has scope, the package will be generated from both the title and the package passed to , not
+# just the package.
+# - If the is always global, the package will be the one passed to , even though it isn't part of it's
+# .
+# - Everything else's package will be what was passed to , even if the title has separator symbols in it.
+#
+sub Package
+ {
+ my ($self) = @_;
+
+ if (NaturalDocs::Topics->TypeInfo($self->Type())->Scope() == ::SCOPE_START())
+ { return $self->Symbol(); }
+ else
+ { return $self->[PACKAGE]; };
+ };
+
+
+# Function: SetPackage
+# Replaces the package the topic appears in. This will behave the same way as the package parameter in . Later calls
+# to will still be generated according to its type-specific behavior.
+sub SetPackage #(package)
+ { $_[0]->[PACKAGE] = $_[1]; };
+
+# Function: Using
+# Returns an arrayref of additional scope available to the topic via "using" statements, or undef if none.
+sub Using
+ { return $_[0]->[USING]; };
+
+# Function: SetUsing
+# Replaces the using arrayref of sope .
+sub SetUsing #(using)
+ { $_[0]->[USING] = $_[1]; };
+
+# Function: Prototype
+# Returns the prototype if one is defined. Will be undef otherwise.
+sub Prototype
+ { return $_[0]->[PROTOTYPE]; };
+
+# Function: SetPrototype
+# Replaces the function or variable prototype.
+sub SetPrototype #(prototype)
+ { $_[0]->[PROTOTYPE] = $_[1]; };
+
+# Function: Summary
+# Returns the topic summary, if it exists, formatted in .
+sub Summary
+ { return $_[0]->[SUMMARY]; };
+
+# Function: Body
+# Returns the topic's body, formatted in . May be undef.
+sub Body
+ { return $_[0]->[BODY]; };
+
+# Function: SetBody
+# Replaces the topic's body, formatted in . May be undef.
+sub SetBody #(body)
+ {
+ my ($self, $body) = @_;
+ $self->[BODY] = $body;
+ };
+
+# Function: LineNumber
+# Returns the line the topic appears at in the file.
+sub LineNumber
+ { return $_[0]->[LINE_NUMBER]; };
+
+
+1;
--
cgit 1.4.1