diff options
Diffstat (limited to 'docs/doctool/Modules')
51 files changed, 0 insertions, 29307 deletions
diff --git a/docs/doctool/Modules/NaturalDocs/Builder.pm b/docs/doctool/Modules/NaturalDocs/Builder.pm deleted file mode 100644 index 7e28fcc2..00000000 --- a/docs/doctool/Modules/NaturalDocs/Builder.pm +++ /dev/null @@ -1,237 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Builder -# -############################################################################### -# -# A package that takes parsed source file and builds the output for it. -# -# Usage and Dependencies: -# -# - <Add()> can be called immediately. -# - <OutputPackages()> and <OutputPackageOf()> can be called once all sub-packages have been registered via <Add()>. -# Since this is normally done in their INIT functions, they should be available to all normal functions immediately. -# -# - Prior to calling <Run()>, <NaturalDocs::Settings>, <NaturalDocs::Project>, <NaturalDocs::Menu>, and -# <NaturalDocs::Parser> must be initialized. <NaturalDocs::Settings->GenerateDirectoryNames()> must be called. -# <NaturalDocs::SymbolTable> and <NaturalDocs::ClassHierarchy> must be initialized and fully resolved. -# -############################################################################### - -# 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; - -use NaturalDocs::Builder::Base; -use NaturalDocs::Builder::HTML; -use NaturalDocs::Builder::FramedHTML; - -package NaturalDocs::Builder; - - -############################################################################### -# Group: Variables - -# -# Array: outputPackages -# -# An array of the output packages available for use. -# -my @outputPackages; - - -############################################################################### -# Group: Functions - - -# -# Function: OutputPackages -# -# Returns an arrayref of the output packages available for use. The arrayref is not a copy of the data, so don't change it. -# -# Add output packages to this list with the <Add()> function. -# -sub OutputPackages - { return \@outputPackages; }; - - -# -# Function: OutputPackageOf -# -# Returns the output package corresponding to the passed command line option, or undef if none. -# -sub OutputPackageOf #(commandLineOption) - { - my ($self, $commandLineOption) = @_; - - $commandLineOption = lc($commandLineOption); - - foreach my $package (@outputPackages) - { - if (lc($package->CommandLineOption()) eq $commandLineOption) - { return $package; }; - }; - - return undef; - }; - - - -# -# Function: Add -# -# Adds an output package to those available for use. All output packages must call this function in order to be recognized. -# -# Parameters: -# -# package - The package name. -# -sub Add #(package) - { - my ($self, $package) = @_; - - # Output packages shouldn't register themselves more than once, so we don't need to check for it. - push @outputPackages, $package; - }; - - -# -# Function: Run -# -# Runs the build process. This must be called *every time* Natural Docs is run, regardless of whether any source files changed -# or not. Some output packages have dependencies on files outside of the source tree that need to be checked. -# -# Since there are multiple stages to the build process, this function will handle its own status messages. There's no need to print -# "Building files..." or something similar beforehand. -# -sub Run - { - my ($self) = @_; - - - # Determine what we're doing. - - my $buildTargets = NaturalDocs::Settings->BuildTargets(); - my $filesToBuild = NaturalDocs::Project->FilesToBuild(); - - my $numberToPurge = scalar keys %{NaturalDocs::Project->FilesToPurge()}; - my $numberToBuild = scalar keys %$filesToBuild; - - my %indexesToBuild; - my %indexesToPurge; - - my $currentIndexes = NaturalDocs::Menu->Indexes(); - my $previousIndexes = NaturalDocs::Menu->PreviousIndexes(); - - foreach my $index (keys %$currentIndexes) - { - if (NaturalDocs::SymbolTable->IndexChanged($index) || !exists $previousIndexes->{$index}) - { - $indexesToBuild{$index} = 1; - }; - }; - - # All indexes that still exist should have been deleted. - foreach my $index (keys %$previousIndexes) - { - if (!exists $currentIndexes->{$index}) - { - $indexesToPurge{$index} = 1; - }; - }; - - my $numberOfIndexesToBuild = scalar keys %indexesToBuild; - my $numberOfIndexesToPurge = scalar keys %indexesToPurge; - - - # Start the build process - - foreach my $buildTarget (@$buildTargets) - { - $buildTarget->Builder()->BeginBuild($numberToPurge || $numberToBuild || $numberOfIndexesToBuild || - $numberOfIndexesToPurge || NaturalDocs::Menu->HasChanged()); - }; - - if ($numberToPurge) - { - NaturalDocs::StatusMessage->Start('Purging ' . $numberToPurge . ' file' . ($numberToPurge > 1 ? 's' : '') . '...', - scalar @$buildTargets); - - foreach my $buildTarget (@$buildTargets) - { - $buildTarget->Builder()->PurgeFiles(); - NaturalDocs::StatusMessage->CompletedItem(); - }; - }; - - if ($numberOfIndexesToPurge) - { - NaturalDocs::StatusMessage->Start('Purging ' . $numberOfIndexesToPurge . - ' index' . ($numberOfIndexesToPurge > 1 ? 'es' : '') . '...', - scalar @$buildTargets); - - foreach my $buildTarget (@$buildTargets) - { - $buildTarget->Builder()->PurgeIndexes(\%indexesToPurge); - NaturalDocs::StatusMessage->CompletedItem(); - }; - }; - - if ($numberToBuild) - { - NaturalDocs::StatusMessage->Start('Building ' . $numberToBuild . ' file' . ($numberToBuild > 1 ? 's' : '') . '...', - $numberToBuild * scalar @$buildTargets); - - foreach my $file (keys %$filesToBuild) - { - my $parsedFile = NaturalDocs::Parser->ParseForBuild($file); - - NaturalDocs::Error->OnStartBuilding($file); - - foreach my $buildTarget (@$buildTargets) - { - $buildTarget->Builder()->BuildFile($file, $parsedFile); - NaturalDocs::StatusMessage->CompletedItem(); - }; - - NaturalDocs::Error->OnEndBuilding($file); - }; - }; - - if ($numberOfIndexesToBuild) - { - NaturalDocs::StatusMessage->Start('Building ' . $numberOfIndexesToBuild . - ' index' . ($numberOfIndexesToBuild != 1 ? 'es' : '') . '...', - $numberOfIndexesToBuild * scalar @$buildTargets); - - foreach my $index (keys %indexesToBuild) - { - foreach my $buildTarget (@$buildTargets) - { - $buildTarget->Builder()->BuildIndex($index); - NaturalDocs::StatusMessage->CompletedItem(); - }; - }; - }; - - if (NaturalDocs::Menu->HasChanged()) - { - if (!NaturalDocs::Settings->IsQuiet()) - { print "Updating menu...\n"; }; - - foreach my $buildTarget (@$buildTargets) - { $buildTarget->Builder()->UpdateMenu(); }; - }; - - foreach my $buildTarget (@$buildTargets) - { - $buildTarget->Builder()->EndBuild($numberToPurge || $numberToBuild || $numberOfIndexesToBuild || - $numberOfIndexesToPurge || NaturalDocs::Menu->HasChanged()); - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Builder/Base.pm b/docs/doctool/Modules/NaturalDocs/Builder/Base.pm deleted file mode 100644 index 2d6cf468..00000000 --- a/docs/doctool/Modules/NaturalDocs/Builder/Base.pm +++ /dev/null @@ -1,316 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Builder::Base -# -############################################################################### -# -# A base class for all Builder output formats. -# -############################################################################### - -# 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::Builder::Base; - - -############################################################################### -# Group: Notes - - -# -# Topic: Implementation -# -# Builder packages are implemented as blessed arrayrefs, not hashrefs. This is done for all objects in Natural Docs for -# efficiency reasons. You create members by defining constants via <NaturalDocs::DefineMembers> and using them as -# indexes into the array. -# - -# -# Topic: Function Order -# -# The functions in the build process will always be called in the following order. -# -# - <BeginBuild()> will always be called. -# - <PurgeFiles()> will be called next only if there's files that need to be purged. -# - <PurgeIndexes()> will be called next only if there's indexes that need to be purged. -# - <BuildFile()> will be called once for each file that needs to be built, if any. -# - <BuildIndex()> will be called once for each index that changed and is part of the menu, if any. -# - <UpdateMenu()> will be called next only if the menu changed. -# - <EndBuild()> will always be called. -# - -# -# Topic: How to Approach -# -# Here's an idea of how to approach making packages for different output types. -# -# -# Multiple Output Files, Embedded Menu: -# -# This example is for when you want to build one output file per source file, each with its own copy of the menu within it. -# This is how <NaturalDocs::Builder::HTML> works. -# -# Make sure you create a function that generates just the menu for a particular source file. We'll need to generate menus for -# both building a file from scratch and for updating the menu on an existing output file, so it's better to give it its own function. -# You may want to surround it with something that can be easily detected in the output file to make replacing easier. -# -# <BeginBuild()> isn't important. You don't need to implement it. -# -# Implement <PurgeFiles()> to delete the output files associated with the purged files. -# -# Implement <PurgeIndexes()> to delete the output files associated with the purged indexes. -# -# Implement <BuildFile()> to create an output file for the parsed source file. Use the menu function described earlier. -# -# Implement <BuildIndex()> to create an output file for each index. Use the menu function described earlier for each page. -# -# Implement <UpdateMenu()> to go through the list of unbuilt files and update their menus. You can get the list from -# <NaturalDocs::Project->UnbuiltFilesWithContent()>. You need to open their output files, replace the menu, and save it back -# to disk. Yes, it would be simpler from a programmer's point of view to just rebuild the file completely, but that would be -# _very_ inefficient since there could potentially be a _lot_ of files in this group. -# -# Also make sure <UpdateMenu()> goes through the unchanged indexes and updates them as well. -# -# <EndBuild()> isn't important. You don't need to implement it. -# -# -# Multiple Output Files, Menu in File: -# -# This example is for when you want to build one output file per source file, but keep the menu in its own separate file. This -# is how <NaturalDocs::Builder::FramedHTML> works. -# -# <BeginBuild()> isn't important. You don't need to implement it. -# -# Implement <PurgeFiles()> to delete the output files associated with the purged files. -# -# Implement <PurgeIndexes()> to delete the output files associated with the purged indexes. -# -# Implement <BuildFile()> to generate an output file from the parsed source file. -# -# Implement <BuildIndex()> to generate an output file for each index. -# -# Implement <UpdateMenu()> to rebuild the menu file. -# -# <EndBuild()> isn't important. You don't need to implement it. -# -# -# Single Output File using Intermediate Files: -# -# This example is for when you want to build one output file, such as a PDF file, but use intermediate files to handle differential -# building. This would be much like how a compiler compiles each source file into a object file, and then a linker stitches them -# all together into the final executable file. -# -# <BeginBuild()> isn't important. You don't need to implement it. -# -# Implement <PurgeFiles()> to delete the intermediate files associated with the purged files. -# -# Implement <PurgeIndexes()> to delete the intermediate files associated with the purged indexes. -# -# Implement <BuildFile()> to generate an intermediate file from the parsed source file. -# -# Implement <BuildIndex()> to generate an intermediate file for the specified index. -# -# Implement <UpdateMenu()> to generate the intermediate file for the menu. -# -# Implement <EndBuild()> so that if the project changed, it stitches the intermediate files together into the final -# output file. Make sure you check the parameter because the function will be called when nothing changes too. -# -# -# Single Output File using Direct Changes: -# -# This example is for when you want to build one output file, such as a PDF file, but engineering it in such a way that you don't -# need to use intermediate files. In other words, you're able to add, delete, and modify entries directly in the output file. -# -# Implement <BeginBuild()> so that if the project changed, it opens the output file and does anything it needs to do -# to get ready for editing. -# -# Implement <PurgeFiles()> to remove the entries associated with the purged files. -# -# Implement <PurgeIndexes()> to remove the entries associated with the purged indexes. -# -# Implement <BuildFile()> to add or replace a section of the output file with a new one generated from the parsed file. -# -# Implement <BuildIndex()> to add or replace an index in the output file with a new one generated from the specified index. -# -# Implement <EndBuild()> so that if the project changed, it saves the output file to disk. -# -# How you handle the menu depends on how the output file references other sections of itself. If it can do so by name, then -# you can implement <UpdateMenu()> to update the menu section of the file and you're done. If it has to reference itself -# by address or offset, it gets trickier. You should skip <UpdateMenu()> and instead rebuild the menu in <EndBuild()> if -# the parameter is true. This lets you do it whenever anything changes in a file, rather than just when the menu -# visibly changes. How you keep track of the locations and how they change is your problem. -# - - -############################################################################### -# -# Group: Required Interface Functions -# -# All Builder classes *must* define these functions. -# - - -# -# Function: INIT -# -# Define this function to call <NaturalDocs::Builder->Add()> so that <NaturalDocs::Builder> knows about this package. -# Packages are defined this way so that new ones can be added without messing around in other code. -# - - -# -# Function: CommandLineOption -# -# Define this function to return the text that should be put in the command line after -o to use this package. It cannot have -# spaces and is not case sensitive. -# -# For example, <NaturalDocs::Builder::HTML> returns 'html' so someone could use -o html [directory] to use that package. -# -sub CommandLineOption - { - NaturalDocs::Error->SoftDeath($_[0] . " didn't define CommandLineOption()."); - }; - - -# -# Function: BuildFile -# -# Define this function to convert a parsed file to this package's output format. This function will be called once for every source -# file that needs to be rebuilt. However, if a file hasn't changed since the last time Natural Docs was run, it will not be sent to -# this function. All packages must support differential build. -# -# Parameters: -# -# sourceFile - The name of the source file. -# parsedFile - The parsed source file, as an arrayref of <NaturalDocs::Parser::ParsedTopic> objects. -# -sub BuildFile #(sourceFile, parsedFile) - { - NaturalDocs::Error->SoftDeath($_[0] . " didn't define BuildFile()."); - }; - - -############################################################################### -# -# Group: Optional Interface Functions -# -# These functions can be implemented but packages are not required to do so. -# - - -# -# Function: New -# -# Creates and returns a new object. -# -# Note that this is the only function where the first parameter will be the package name, not the object itself. -# -sub New - { - my $package = shift; - - my $object = [ ]; - bless $object, $package; - - return $object; - }; - - -# -# Function: BeginBuild -# -# Define this function if the package needs to do anything at the beginning of the build process. This function will be called -# every time Natural Docs is run, even if the project hasn't changed. This allows you to manage dependencies specific -# to the output format that may change independently from the source tree and menu. For example, -# <NaturalDocs::Builder::HTML> needs to keep the CSS files in sync regardless of whether the source tree changed or not. -# -# Parameters: -# -# hasChanged - Whether the project has changed, such as source files or the menu file. If false, nothing else is going to be -# called except <EndBuild()>. -# -sub BeginBuild #(hasChanged) - { - }; - - -# -# Function: EndBuild -# -# Define this function if the package needs to do anything at the end of the build process. This function will be called every time -# Natural Docs is run, even if the project hasn't changed. This allows you to manage dependencies specific to the output -# format that may change independently from the source tree. For example, <NaturalDocs::Builder::HTML> needs to keep the -# CSS files in sync regardless of whether the source tree changed or not. -# -# Parameters: -# -# hasChanged - Whether the project has changed, such as source files or the menu file. If false, the only other function that -# was called was <BeginBuild()>. -# -sub EndBuild #(hasChanged) - { - }; - - -# -# Function: BuildIndex -# -# Define this function to create an index for the passed topic. You can get the index from -# <NaturalDocs::SymbolTable->Index()>. -# -# The reason it's not passed directly to this function is because indexes may be time-consuming to create. As such, they're -# generated on demand because some output packages may choose not to implement them. -# -# Parameters: -# -# topic - The <TopicType> to limit the index by. -# -sub BuildIndex #(topic) - { - }; - -# -# Function: PurgeFiles -# -# Define this function to make the package remove all output related to the passed files. These files no longer have Natural Docs -# content. -# -# Parameters: -# -# files - An existence hashref of the files to purge. -# -sub PurgeFiles #(files) - { - }; - - -# -# Function: PurgeIndexes -# -# Define this function to make the package remove all output related to the passed indexes. These indexes are no longer part -# of the menu. -# -# Parameters: -# -# indexes - An existence hashref of the <TopicTypes> of the indexes to purge. -# -sub PurgeIndexes #(indexes) - { - }; - - -# -# Function: UpdateMenu -# -# Define this function to make the package update the menu. It will only be called if the menu changed. -# -sub UpdateMenu - { - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Builder/FramedHTML.pm b/docs/doctool/Modules/NaturalDocs/Builder/FramedHTML.pm deleted file mode 100644 index 7b615e4b..00000000 --- a/docs/doctool/Modules/NaturalDocs/Builder/FramedHTML.pm +++ /dev/null @@ -1,294 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Builder::FramedHTML -# -############################################################################### -# -# A package that generates output in HTML with frames. -# -# All functions are called with Package->Function() notation. -# -############################################################################### - -# 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::Builder::FramedHTML; - -use base 'NaturalDocs::Builder::HTMLBase'; - - -############################################################################### -# Group: Implemented Interface Functions - - -# -# Function: INIT -# -# Registers the package with <NaturalDocs::Builder>. -# -sub INIT - { - NaturalDocs::Builder->Add(__PACKAGE__); - }; - - -# -# Function: CommandLineOption -# -# Returns the option to follow -o to use this package. In this case, "html". -# -sub CommandLineOption - { - return 'FramedHTML'; - }; - - -# -# Function: BuildFile -# -# Builds the output file from the parsed source file. -# -# Parameters: -# -# sourcefile - The <FileName> of the source file. -# parsedFile - An arrayref of the source file as <NaturalDocs::Parser::ParsedTopic> objects. -# -sub BuildFile #(sourceFile, parsedFile) - { - my ($self, $sourceFile, $parsedFile) = @_; - - my $outputFile = $self->OutputFileOf($sourceFile); - - - # 99.99% of the time the output directory will already exist, so this will actually be more efficient. It only won't exist - # if a new file was added in a new subdirectory and this is the first time that file was ever parsed. - if (!open(OUTPUTFILEHANDLE, '>' . $outputFile)) - { - NaturalDocs::File->CreatePath( NaturalDocs::File->NoFileName($outputFile) ); - - open(OUTPUTFILEHANDLE, '>' . $outputFile) - or die "Couldn't create output file " . $outputFile . "\n"; - }; - - print OUTPUTFILEHANDLE - - - - # IE 6 doesn't like any doctype here at all. Add one (strict or transitional doesn't matter) and it makes the page slightly too - # wide for the frame. Mozilla and Opera handle it like champs either way because they Don't Suck(tm). - - # '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ' - # . '"http://www.w3.org/TR/REC-html40/loose.dtd">' . "\n\n" - - '<html><head>' - - . (NaturalDocs::Settings->CharSet() ? - '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') - - . '<title>' - . $self->BuildTitle($sourceFile) - . '</title>' - - . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($outputFile, $self->MainCSSFile(), 1) . '">' - - . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->MainJavaScriptFile(), 1) . '"></script>' - - . '</head><body class=FramedContentPage onLoad="NDOnLoad()">' - . $self->OpeningBrowserStyles() - - . $self->StandardComments() - - . $self->BuildContent($sourceFile, $parsedFile) - - . $self->BuildToolTips() - - . $self->ClosingBrowserStyles() - . '</body></html>'; - - - close(OUTPUTFILEHANDLE); - }; - - -# -# Function: BuildIndex -# -# Builds an index for the passed type. -# -# Parameters: -# -# type - The <TopicType> to limit the index to, or undef if none. -# -sub BuildIndex #(type) - { - my ($self, $type) = @_; - - my $indexTitle = $self->IndexTitleOf($type); - my $indexFile = $self->IndexFileOf($type); - - my $startPage = - - '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ' - . '"http://www.w3.org/TR/REC-html40/loose.dtd">' . "\n\n" - - . '<html><head>' - - . (NaturalDocs::Settings->CharSet() ? - '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') - - . '<title>'; - - if (defined NaturalDocs::Menu->Title()) - { $startPage .= $self->StringToHTML(NaturalDocs::Menu->Title()) . ' - '; }; - - $startPage .= - $indexTitle - . '</title>' - - . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($indexFile, $self->MainCSSFile(), 1) . '">' - - . '<script language=JavaScript src="' . $self->MakeRelativeURL($indexFile, $self->MainJavaScriptFile(), 1) . '"></script>' - - . '</head><body class=FramedIndexPage onLoad="NDOnLoad()">' - . $self->OpeningBrowserStyles() - - . $self->StandardComments() - - . '<div class=IPageTitle>' - . $indexTitle - . '</div>'; - - - my $endPage = $self->ClosingBrowserStyles() . '</body></html>'; - - - my $pageCount = $self->BuildIndexPages($type, NaturalDocs::SymbolTable->Index($type), $startPage, $endPage); - $self->PurgeIndexFiles($type, $pageCount + 1); - }; - - -# -# Function: UpdateMenu -# -# Builds the menu file. Also generates index.html. -# -sub UpdateMenu - { - my $self = shift; - - my $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self); - my $outputFile = NaturalDocs::File->JoinPaths($outputDirectory, 'menu.html'); - - - open(OUTPUTFILEHANDLE, '>' . $outputFile) - or die "Couldn't create output file " . $outputFile . "\n"; - - my $title = 'Menu'; - if (defined $title) - { $title .= ' - ' . NaturalDocs::Menu->Title(); }; - - $title = $self->StringToHTML($title); - - - print OUTPUTFILEHANDLE - - - '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ' - . '"http://www.w3.org/TR/REC-html40/loose.dtd">' . "\n\n" - - . '<html><head>' - - . (NaturalDocs::Settings->CharSet() ? - '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') - - . '<title>' - . $title - . '</title>' - - . '<base target="Content">' - - . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($outputFile, $self->MainCSSFile(), 1) . '">' - - . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->MainJavaScriptFile(), 1) . '"></script>' - - . '</head><body class=FramedMenuPage onLoad="NDOnLoad()">' - . $self->OpeningBrowserStyles() - - . $self->StandardComments() - - . $self->BuildMenu(undef, undef, 1) - - . '<div class=Footer>' - . $self->BuildFooter() - . '</div>' - - . $self->ClosingBrowserStyles() - . '</body></html>'; - - - close(OUTPUTFILEHANDLE); - - - # Update index.html - - my $firstMenuEntry = $self->FindFirstFile(); - my $indexFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index.html' ); - - # We have to check because it's possible that there may be no files with Natural Docs content and thus no files on the menu. - if (defined $firstMenuEntry) - { - open(INDEXFILEHANDLE, '>' . $indexFile) - or die "Couldn't create output file " . $indexFile . ".\n"; - - print INDEXFILEHANDLE - - '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" ' - . '"http://www.w3.org/TR/REC-html40/frameset.dtd">' - - . '<html>' - - . '<head>' - - . (NaturalDocs::Settings->CharSet() ? - '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') - - . '<title>' - . $self->StringToHTML(NaturalDocs::Menu->Title()) - . '</title>' - - . '</head>' - - . $self->StandardComments() - - . '<frameset cols="185,*">' - . '<frame name=Menu src="menu.html">' - . '<frame name=Content src="' - . $self->MakeRelativeURL($indexFile, $self->OutputFileOf($firstMenuEntry->Target()), 1) . '">' - . '</frameset>' - - . '<noframes>' - . 'This documentation was designed for use with frames. However, you can still use it by ' - . '<a href="menu.html">starting from the menu page</a>.' - . "<script language=JavaScript><!--\n" - . 'location.href="menu.html";' - . "\n// --></script>" - . '</noframes>' - - . '</html>'; - - close INDEXFILEHANDLE; - } - - elsif (-e $indexFile) - { - unlink($indexFile); - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Builder/HTML.pm b/docs/doctool/Modules/NaturalDocs/Builder/HTML.pm deleted file mode 100644 index 92b4bd7f..00000000 --- a/docs/doctool/Modules/NaturalDocs/Builder/HTML.pm +++ /dev/null @@ -1,363 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Builder::HTML -# -############################################################################### -# -# A package that generates output in HTML. -# -# All functions are called with Package->Function() notation. -# -############################################################################### - -# 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::Builder::HTML; - -use base 'NaturalDocs::Builder::HTMLBase'; - - -############################################################################### -# Group: Implemented Interface Functions - - -# -# Function: INIT -# -# Registers the package with <NaturalDocs::Builder>. -# -sub INIT - { - NaturalDocs::Builder->Add(__PACKAGE__); - }; - - -# -# Function: CommandLineOption -# -# Returns the option to follow -o to use this package. In this case, "html". -# -sub CommandLineOption - { - return 'HTML'; - }; - - -# -# Function: BuildFile -# -# Builds the output file from the parsed source file. -# -# Parameters: -# -# sourcefile - The <FileName> of the source file. -# parsedFile - An arrayref of the source file as <NaturalDocs::Parser::ParsedTopic> objects. -# -sub BuildFile #(sourceFile, parsedFile) - { - my ($self, $sourceFile, $parsedFile) = @_; - - my $outputFile = $self->OutputFileOf($sourceFile); - - - # 99.99% of the time the output directory will already exist, so this will actually be more efficient. It only won't exist - # if a new file was added in a new subdirectory and this is the first time that file was ever parsed. - if (!open(OUTPUTFILEHANDLE, '>' . $outputFile)) - { - NaturalDocs::File->CreatePath( NaturalDocs::File->NoFileName($outputFile) ); - - open(OUTPUTFILEHANDLE, '>' . $outputFile) - or die "Couldn't create output file " . $outputFile . "\n"; - }; - - print OUTPUTFILEHANDLE - - - '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" ' - . '"http://www.w3.org/TR/REC-html40/strict.dtd">' . "\n\n" - - . '<html><head>' - - . (NaturalDocs::Settings->CharSet() ? - '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') - - . '<title>' - . $self->BuildTitle($sourceFile) - . '</title>' - - . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($outputFile, $self->MainCSSFile(), 1) . '">' - - . '<script language=JavaScript src="' . $self->MakeRelativeURL($outputFile, $self->MainJavaScriptFile(), 1) . '"></script>' - - . '</head><body class=UnframedPage onLoad="NDOnLoad()">' - . $self->OpeningBrowserStyles() - - . $self->StandardComments() - - # I originally had this part done in CSS, but there were too many problems. Back to good old HTML tables. - . '<table border=0 cellspacing=0 cellpadding=0 width=100%><tr>' - - . '<td class=MenuSection valign=top>' - - . $self->BuildMenu($sourceFile, undef, undef) - - . '</td>' . "\n\n" - - . '<td class=ContentSection valign=top>' - - . $self->BuildContent($sourceFile, $parsedFile) - - . '</td>' . "\n\n" - - . '</tr></table>' - - . '<div class=Footer>' - . $self->BuildFooter() - . '</div>' - - . $self->BuildToolTips() - - . $self->ClosingBrowserStyles() - . '</body></html>'; - - - close(OUTPUTFILEHANDLE); - }; - - -# -# Function: BuildIndex -# -# Builds an index for the passed type. -# -# Parameters: -# -# type - The <TopicType> to limit the index to, or undef if none. -# -sub BuildIndex #(type) - { - my ($self, $type) = @_; - - my $indexTitle = $self->IndexTitleOf($type); - my $indexFile = $self->IndexFileOf($type); - - my $startPage = - - '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" ' - . '"http://www.w3.org/TR/REC-html40/strict.dtd">' . "\n\n" - - . '<html><head>' - - . (NaturalDocs::Settings->CharSet() ? - '<meta http-equiv="Content-Type" content="text/html; charset=' . NaturalDocs::Settings->CharSet() . '">' : '') - - . '<title>' - . $indexTitle; - - if (defined NaturalDocs::Menu->Title()) - { $startPage .= ' - ' . $self->StringToHTML(NaturalDocs::Menu->Title()); }; - - $startPage .= - '</title>' - - . '<link rel="stylesheet" type="text/css" href="' . $self->MakeRelativeURL($indexFile, $self->MainCSSFile(), 1) . '">' - - . '<script language=JavaScript src="' . $self->MakeRelativeURL($indexFile, $self->MainJavaScriptFile(), 1) . '"></script>' - - . '</head><body class=UnframedPage onLoad="NDOnLoad()">' - . $self->OpeningBrowserStyles() - - . $self->StandardComments() - - # I originally had this part done in CSS, but there were too many problems. Back to good old HTML tables. - . '<table border=0 cellspacing=0 cellpadding=0 width=100%><tr>' - - . '<td class=MenuSection valign=top>' - - . $self->BuildMenu(undef, $type, undef) - - . '</td>' - - . '<td class=IndexSection valign=top>' - . '<div class=IPageTitle>' - . $indexTitle - . '</div>'; - - - my $endPage = - '</td>' - - . '</tr></table>' - - . '<div class=Footer>' - . $self->BuildFooter() - . '</div>' - - . $self->ClosingBrowserStyles() - . '</body></html>'; - - - my $pageCount = $self->BuildIndexPages($type, NaturalDocs::SymbolTable->Index($type), $startPage, $endPage); - $self->PurgeIndexFiles($type, $pageCount + 1); - }; - - -# -# Function: UpdateMenu -# -# Updates the menu in all the output files that weren't rebuilt. Also generates index.html. -# -sub UpdateMenu - { - my $self = shift; - - - # Update the menu on unbuilt files. - - my $filesToUpdate = NaturalDocs::Project->UnbuiltFilesWithContent(); - - foreach my $sourceFile (keys %$filesToUpdate) - { - $self->UpdateFile($sourceFile); - }; - - - # Update the menu on unchanged index files. - - my $indexes = NaturalDocs::Menu->Indexes(); - - foreach my $index (keys %$indexes) - { - if (!NaturalDocs::SymbolTable->IndexChanged($index)) - { - $self->UpdateIndex($index); - }; - }; - - - # Update index.html - - my $firstMenuEntry = $self->FindFirstFile(); - my $indexFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index.html' ); - - # We have to check because it's possible that there may be no files with Natural Docs content and thus no files on the menu. - if (defined $firstMenuEntry) - { - open(INDEXFILEHANDLE, '>' . $indexFile) - or die "Couldn't create output file " . $indexFile . ".\n"; - - print INDEXFILEHANDLE - '<html><head>' - . '<meta http-equiv="Refresh" CONTENT="0; URL=' - . $self->MakeRelativeURL( NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index.html'), - $self->OutputFileOf($firstMenuEntry->Target()), 1 ) . '">' - . '</head></html>'; - - close INDEXFILEHANDLE; - } - - elsif (-e $indexFile) - { - unlink($indexFile); - }; - }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: UpdateFile -# -# Updates an output file. Replaces the menu, HTML title, and footer. It opens the output file, makes the changes, and saves it -# back to disk, which is much quicker than rebuilding the file from scratch if these were the only things that changed. -# -# Parameters: -# -# sourceFile - The source <FileName>. -# -sub UpdateFile #(sourceFile) - { - my ($self, $sourceFile) = @_; - - my $outputFile = $self->OutputFileOf($sourceFile); - - if (open(OUTPUTFILEHANDLE, '<' . $outputFile)) - { - my $content; - - read(OUTPUTFILEHANDLE, $content, -s OUTPUTFILEHANDLE); - close(OUTPUTFILEHANDLE); - - - $content =~ s{<title>[^<]*<\/title>}{'<title>' . $self->BuildTitle($sourceFile) . '</title>'}e; - - $content =~ s/<!--START_ND_MENU-->.*?<!--END_ND_MENU-->/$self->BuildMenu($sourceFile, undef, undef)/es; - - $content =~ s/<!--START_ND_FOOTER-->.*?<!--END_ND_FOOTER-->/$self->BuildFooter()/e; - - - open(OUTPUTFILEHANDLE, '>' . $outputFile); - print OUTPUTFILEHANDLE $content; - close(OUTPUTFILEHANDLE); - }; - }; - - -# -# Function: UpdateIndex -# -# Updates an index's output file. Replaces the menu and footer. It opens the output file, makes the changes, and saves it -# back to disk, which is much quicker than rebuilding the file from scratch if these were the only things that changed. -# -# Parameters: -# -# type - The index <TopicType>, or undef if none. -# -sub UpdateIndex #(type) - { - my ($self, $type) = @_; - - my $page = 1; - - my $outputFile = $self->IndexFileOf($type, $page); - - my $newMenu = $self->BuildMenu(undef, $type, undef); - my $newFooter = $self->BuildFooter(); - - while (-e $outputFile) - { - open(OUTPUTFILEHANDLE, '<' . $outputFile) - or die "Couldn't open output file " . $outputFile . ".\n"; - - my $content; - - read(OUTPUTFILEHANDLE, $content, -s OUTPUTFILEHANDLE); - close(OUTPUTFILEHANDLE); - - - $content =~ s/<!--START_ND_MENU-->.*?<!--END_ND_MENU-->/$newMenu/es; - - $content =~ s/<div class=Footer>.*<\/div>/"<div class=Footer>" . $newFooter . "<\/div>"/e; - - - open(OUTPUTFILEHANDLE, '>' . $outputFile) - or die "Couldn't save output file " . $outputFile . ".\n"; - - print OUTPUTFILEHANDLE $content; - close(OUTPUTFILEHANDLE); - - $page++; - $outputFile = $self->IndexFileOf($type, $page); - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Builder/HTMLBase.pm b/docs/doctool/Modules/NaturalDocs/Builder/HTMLBase.pm deleted file mode 100644 index 52653a90..00000000 --- a/docs/doctool/Modules/NaturalDocs/Builder/HTMLBase.pm +++ /dev/null @@ -1,3075 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Builder::HTMLBase -# -############################################################################### -# -# A base package for all the shared functionality in <NaturalDocs::Builder::HTML> and -# <NaturalDocs::Builder::FramedHTML>. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - - -use Tie::RefHash; - -use strict; -use integer; - -package NaturalDocs::Builder::HTMLBase; - -use base 'NaturalDocs::Builder::Base'; - - -############################################################################### -# Group: Package Variables -# These variables are shared by all instances of the package so don't change them. - - -# -# handle: FH_CSS_FILE -# -# The file handle to use when updating CSS files. -# - - -# -# Hash: abbreviations -# -# An existence hash of acceptable abbreviations. These are words that <AddDoubleSpaces()> won't put a second space -# after when followed by period-whitespace-capital letter. Yes, this is seriously over-engineered. -# -my %abbreviations = ( mr => 1, mrs => 1, ms => 1, dr => 1, - rev => 1, fr => 1, 'i.e' => 1, - maj => 1, gen => 1, pres => 1, sen => 1, rep => 1, - n => 1, s => 1, e => 1, w => 1, ne => 1, se => 1, nw => 1, sw => 1 ); - -# -# array: indexHeadings -# -# An array of the headings of all the index sections. First is for symbols, second for numbers, and the rest for each letter. -# -my @indexHeadings = ( '$#!', '0-9', 'A' .. 'Z' ); - -# -# array: indexAnchors -# -# An array of the HTML anchors of all the index sections. First is for symbols, second for numbers, and the rest for each letter. -# -my @indexAnchors = ( 'Symbols', 'Numbers', 'A' .. 'Z' ); - -# -# bool: saidUpdatingCSSFile -# -# Whether the status message "Updating CSS file..." has been displayed. We only want to print it once, no matter how many -# HTML-based targets we are building. -# -my $saidUpdatingCSSFile; - -# -# constant: ADD_HIDDEN_BREAKS -# -# Just a synonym for "1" so that setting the flag on <StringToHTML()> is clearer in the calling code. -# -use constant ADD_HIDDEN_BREAKS => 1; - - -############################################################################### -# Group: ToolTip Package Variables -# -# These variables are for the tooltip generation functions only. Since they're reset on every call to <BuildContent()> and -# <BuildIndexPages()>, and are only used by them and their support functions, they can be shared by all instances of the -# package. - -# -# int: tooltipLinkNumber -# -# A number used as part of the ID for each link that has a tooltip. Should be incremented whenever one is made. -# -my $tooltipLinkNumber; - -# -# int: tooltipNumber -# -# A number used as part of the ID for each tooltip. Should be incremented whenever one is made. -# -my $tooltipNumber; - -# -# hash: tooltipSymbolsToNumbers -# -# A hash that maps the tooltip symbols to their assigned numbers. -# -my %tooltipSymbolsToNumbers; - -# -# string: tooltipHTML -# -# The generated tooltip HTML. -# -my $tooltipHTML; - - -############################################################################### -# Group: Menu Package Variables -# -# These variables are for the menu generation functions only. Since they're reset on every call to <BuildMenu()> and are -# only used by it and its support functions, they can be shared by all instances of the package. -# - - -# -# hash: prebuiltMenus -# -# A hash that maps output directonies to menu HTML already built for it. There will be no selection or JavaScript in the menus. -# -my %prebuiltMenus; - - -# -# bool: menuNumbersAndLengthsDone -# -# Set when the variables that only need to be calculated for the menu once are done. This includes <menuGroupNumber>, -# <menuLength>, <menuGroupLengths>, and <menuGroupNumbers>, and <menuRootLength>. -# -my $menuNumbersAndLengthsDone; - - -# -# int: menuGroupNumber -# -# The current menu group number. Each time a group is created, this is incremented so that each one will be unique. -# -my $menuGroupNumber; - - -# -# int: menuLength -# -# The length of the entire menu, fully expanded. The value is computed from the <Menu Length Constants>. -# -my $menuLength; - - -# -# hash: menuGroupLengths -# -# A hash of the length of each group, *not* including any subgroup contents. The keys are references to each groups' -# <NaturalDocs::Menu::Entry> object, and the values are their lengths computed from the <Menu Length Constants>. -# -my %menuGroupLengths; -tie %menuGroupLengths, 'Tie::RefHash'; - - -# -# hash: menuGroupNumbers -# -# A hash of the number of each group, as managed by <menuGroupNumber>. The keys are references to each groups' -# <NaturalDocs::Menu::Entry> object, and the values are the number. -# -my %menuGroupNumbers; -tie %menuGroupNumbers, 'Tie::RefHash'; - - -# -# int: menuRootLength -# -# The length of the top-level menu entries without expansion. The value is computed from the <Menu Length Constants>. -# -my $menuRootLength; - - -# -# constants: Menu Length Constants -# -# Constants used to approximate the lengths of the menu or its groups. -# -# MENU_TITLE_LENGTH - The length of the title. -# MENU_SUBTITLE_LENGTH - The length of the subtitle. -# MENU_FILE_LENGTH - The length of one file entry. -# MENU_GROUP_LENGTH - The length of one group entry. -# MENU_TEXT_LENGTH - The length of one text entry. -# MENU_LINK_LENGTH - The length of one link entry. -# -# MENU_LENGTH_LIMIT - The limit of the menu's length. If the total length surpasses this limit, groups that aren't required -# to be open to show the selection will default to closed on browsers that support it. -# -use constant MENU_TITLE_LENGTH => 3; -use constant MENU_SUBTITLE_LENGTH => 1; -use constant MENU_FILE_LENGTH => 1; -use constant MENU_GROUP_LENGTH => 2; # because it's a line and a blank space -use constant MENU_TEXT_LENGTH => 1; -use constant MENU_LINK_LENGTH => 1; -use constant MENU_INDEX_LENGTH => 1; - -use constant MENU_LENGTH_LIMIT => 35; - - -############################################################################### -# Group: Implemented Interface Functions -# -# The behavior of these functions is shared between HTML output formats. -# - - -# -# Function: PurgeFiles -# -# Deletes the output files associated with the purged source files. -# -sub PurgeFiles - { - my $self = shift; - - my $filesToPurge = NaturalDocs::Project->FilesToPurge(); - - # Combine directories into a hash to remove duplicate work. - my %directoriesToPurge; - - foreach my $file (keys %$filesToPurge) - { - # It's possible that there may be files there that aren't in a valid input directory anymore. They won't generate an output - # file name so we need to check for undef. - my $outputFile = $self->OutputFileOf($file); - if (defined $outputFile) - { - unlink($outputFile); - $directoriesToPurge{ NaturalDocs::File->NoFileName($outputFile) } = 1; - }; - }; - - foreach my $directory (keys %directoriesToPurge) - { - NaturalDocs::File->RemoveEmptyTree($directory, NaturalDocs::Settings->OutputDirectoryOf($self)); - }; - }; - - -# -# Function: PurgeIndexes -# -# Deletes the output files associated with the purged source files. -# -# Parameters: -# -# indexes - An existence hashref of the index types to purge. The keys are the <TopicTypes> or * for the general index. -# -sub PurgeIndexes #(indexes) - { - my ($self, $indexes) = @_; - - foreach my $index (keys %$indexes) - { - $self->PurgeIndexFiles($index, undef); - }; - }; - - -# -# Function: BeginBuild -# -# Creates the necessary subdirectories in the output directory. -# -sub BeginBuild #(hasChanged) - { - my ($self, $hasChanged) = @_; - - foreach my $directory ( $self->JavaScriptDirectory(), $self->CSSDirectory(), $self->IndexDirectory() ) - { - if (!-d $directory) - { NaturalDocs::File->CreatePath($directory); }; - }; - }; - - -# -# Function: EndBuild -# -# Synchronizes the projects CSS and JavaScript files. -# -sub EndBuild #(hasChanged) - { - my ($self, $hasChanged) = @_; - - - # Update the style sheets. - - my $styles = NaturalDocs::Settings->Styles(); - my $changed; - - my $cssDirectory = $self->CSSDirectory(); - my $mainCSSFile = $self->MainCSSFile(); - - for (my $i = 0; $i < scalar @$styles; $i++) - { - my $outputCSSFile; - - if (scalar @$styles == 1) - { $outputCSSFile = $mainCSSFile; } - else - { $outputCSSFile = NaturalDocs::File->JoinPaths($cssDirectory, ($i + 1) . '.css'); }; - - - my $masterCSSFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), $styles->[$i] . '.css' ); - - if (! -e $masterCSSFile) - { $masterCSSFile = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->StyleDirectory(), $styles->[$i] . '.css' ); }; - - # We check both the date and the size in case the user switches between two styles which just happen to have the same - # date. Should rarely happen, but it might. - if (! -e $outputCSSFile || - (stat($masterCSSFile))[9] != (stat($outputCSSFile))[9] || - -s $masterCSSFile != -s $outputCSSFile) - { - if (!NaturalDocs::Settings->IsQuiet() && !$saidUpdatingCSSFile) - { - print "Updating CSS file...\n"; - $saidUpdatingCSSFile = 1; - }; - - NaturalDocs::File->Copy($masterCSSFile, $outputCSSFile); - - $changed = 1; - }; - }; - - - my $deleteFrom; - - if (scalar @$styles == 1) - { $deleteFrom = 1; } - else - { $deleteFrom = scalar @$styles + 1; }; - - for (;;) - { - my $file = NaturalDocs::File->JoinPaths($cssDirectory, $deleteFrom . '.css'); - - if (! -e $file) - { last; }; - - unlink ($file); - $deleteFrom++; - - $changed = 1; - }; - - - if ($changed) - { - if (scalar @$styles > 1) - { - open(FH_CSS_FILE, '>' . $mainCSSFile); - - for (my $i = 0; $i < scalar @$styles; $i++) - { - print FH_CSS_FILE '@import URL("' . ($i + 1) . '.css");' . "\n"; - }; - - close(FH_CSS_FILE); - }; - }; - - - - # Update the JavaScript file. - - my $jsMaster = NaturalDocs::File->JoinPaths( NaturalDocs::Settings->JavaScriptDirectory(), 'NaturalDocs.js' ); - my $jsOutput = $self->MainJavaScriptFile(); - - # We check both the date and the size in case the user switches between two styles which just happen to have the same - # date. Should rarely happen, but it might. - if (! -e $jsOutput || - (stat($jsMaster))[9] != (stat($jsOutput))[9] || - -s $jsMaster != -s $jsOutput) - { - NaturalDocs::File->Copy($jsMaster, $jsOutput); - }; - }; - - - -############################################################################### -# Group: Section Functions - - -# -# function: BuildTitle -# -# Builds and returns the HTML page title of a file. -# -# Parameters: -# -# sourceFile - The source <FileName> to build the title of. -# -# Returns: -# -# The source file's title in HTML. -# -sub BuildTitle #(sourceFile) - { - my ($self, $sourceFile) = @_; - - # If we have a menu title, the page title is [menu title] - [file title]. Otherwise it is just [file title]. - - my $title = NaturalDocs::Project->DefaultMenuTitleOf($sourceFile); - - my $menuTitle = NaturalDocs::Menu->Title(); - if (defined $menuTitle && $menuTitle ne $title) - { $title .= ' - ' . $menuTitle; }; - - $title = $self->StringToHTML($title); - - return $title; - }; - -# -# function: BuildMenu -# -# Builds and returns the side menu of a file. -# -# Parameters: -# -# sourceFile - The source <FileName> to use if you're looking for a source file. -# indexType - The index <TopicType> to use if you're looking for an index. -# isFramed - Whether the menu will appear in a frame. If so, it assumes the <base> HTML tag is set to make links go to the -# appropriate frame. -# -# Both sourceFile and indexType may be undef. -# -# Returns: -# -# The side menu in HTML. -# -sub BuildMenu #(FileName sourceFile, TopicType indexType, bool isFramed) -> string htmlMenu - { - my ($self, $sourceFile, $indexType, $isFramed) = @_; - - if (!$menuNumbersAndLengthsDone) - { - $menuGroupNumber = 1; - $menuLength = 0; - %menuGroupLengths = ( ); - %menuGroupNumbers = ( ); - $menuRootLength = 0; - }; - - my $outputDirectory; - - if ($sourceFile) - { $outputDirectory = NaturalDocs::File->NoFileName( $self->OutputFileOf($sourceFile) ); } - elsif ($indexType) - { $outputDirectory = NaturalDocs::File->NoFileName( $self->IndexFileOf($indexType) ); } - else - { $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self); }; - - - # Comment needed for UpdateFile(). - my $output = '<!--START_ND_MENU-->'; - - - if (!exists $prebuiltMenus{$outputDirectory}) - { - my $segmentOutput; - - ($segmentOutput, $menuRootLength) = - $self->BuildMenuSegment($outputDirectory, $isFramed, NaturalDocs::Menu->Content()); - - my $titleOutput; - - my $menuTitle = NaturalDocs::Menu->Title(); - if (defined $menuTitle) - { - if (!$menuNumbersAndLengthsDone) - { $menuLength += MENU_TITLE_LENGTH; }; - - $menuRootLength += MENU_TITLE_LENGTH; - - $titleOutput .= - '<div class=MTitle>' - . $self->StringToHTML($menuTitle); - - my $menuSubTitle = NaturalDocs::Menu->SubTitle(); - if (defined $menuSubTitle) - { - if (!$menuNumbersAndLengthsDone) - { $menuLength += MENU_SUBTITLE_LENGTH; }; - - $menuRootLength += MENU_SUBTITLE_LENGTH; - - $titleOutput .= - '<div class=MSubTitle>' - . $self->StringToHTML($menuSubTitle) - . '</div>'; - }; - - $titleOutput .= - '</div>'; - }; - - $prebuiltMenus{$outputDirectory} = $titleOutput . $segmentOutput; - $output .= $titleOutput . $segmentOutput; - } - else - { $output .= $prebuiltMenus{$outputDirectory}; }; - - - # Highlight the menu selection. - - if ($sourceFile) - { - # Dependency: This depends on how BuildMenuSegment() formats file entries. - my $outputFile = $self->OutputFileOf($sourceFile); - my $tag = '<div class=MFile><a href="' . $self->MakeRelativeURL($outputDirectory, $outputFile) . '">'; - my $tagIndex = index($output, $tag); - - if ($tagIndex != -1) - { - my $endIndex = index($output, '</a>', $tagIndex); - - substr($output, $endIndex, 4, ''); - substr($output, $tagIndex, length($tag), '<div class=MFile id=MSelected>'); - }; - } - elsif ($indexType) - { - # Dependency: This depends on how BuildMenuSegment() formats index entries. - my $outputFile = $self->IndexFileOf($indexType); - my $tag = '<div class=MIndex><a href="' . $self->MakeRelativeURL($outputDirectory, $outputFile) . '">'; - my $tagIndex = index($output, $tag); - - if ($tagIndex != -1) - { - my $endIndex = index($output, '</a>', $tagIndex); - - substr($output, $endIndex, 4, ''); - substr($output, $tagIndex, length($tag), '<div class=MIndex id=MSelected>'); - }; - }; - - - # If the completely expanded menu is too long, collapse all the groups that aren't in the selection hierarchy or near the - # selection. By doing this instead of having them default to closed via CSS, any browser that doesn't support changing this at - # runtime will keep the menu entirely open so that its still usable. - - if ($menuLength > MENU_LENGTH_LIMIT()) - { - my $menuSelectionHierarchy = $self->GetMenuSelectionHierarchy($sourceFile, $indexType); - - my $toExpand = $self->ExpandMenu($sourceFile, $indexType, $menuSelectionHierarchy, $menuRootLength); - - $output .= - - '<script language=JavaScript><!--' . "\n" - - # Using ToggleMenu here causes IE to sometimes say display is nothing instead of "block" or "none" on the first click. - # Whatever. This is just as good. - - . 'if (document.getElementById)' - . '{'; - - if (scalar @$toExpand) - { - $output .= - - 'for (var menu = 1; menu < ' . $menuGroupNumber . '; menu++)' - . '{' - . 'if (menu != ' . join(' && menu != ', @$toExpand) . ')' - . '{' - . 'document.getElementById("MGroupContent" + menu).style.display = "none";' - . '};' - . '};' - } - else - { - $output .= - - 'for (var menu = 1; menu < ' . $menuGroupNumber . '; menu++)' - . '{' - . 'document.getElementById("MGroupContent" + menu).style.display = "none";' - . '};' - }; - - $output .= - '}' - - . '// --></script>'; - }; - - # Comment needed for UpdateFile(). - $output .= '<!--END_ND_MENU-->'; - - $menuNumbersAndLengthsDone = 1; - - return $output; - }; - - -# -# Function: BuildMenuSegment -# -# A recursive function to build a segment of the menu. *Remember to reset the <Menu Package Variables> before calling this -# for the first time.* -# -# Parameters: -# -# outputDirectory - The output directory the menu is being built for. -# isFramed - Whether the menu will be in a HTML frame or not. Assumes that if it is, the <base> HTML tag will be set so that -# links are directed to the proper frame. -# menuSegment - An arrayref specifying the segment of the menu to build. Either pass the menu itself or the contents -# of a group. -# -# Returns: -# -# The array ( menuHTML, length ). -# -# menuHTML - The menu segment in HTML. -# groupLength - The length of the group, *not* including the contents of any subgroups, as computed from the -# <Menu Length Constants>. -# -sub BuildMenuSegment #(outputDirectory, isFramed, menuSegment) - { - my ($self, $outputDirectory, $isFramed, $menuSegment) = @_; - - my ($output, $groupLength); - - foreach my $entry (@$menuSegment) - { - if ($entry->Type() == ::MENU_GROUP()) - { - my ($entryOutput, $entryLength) = - $self->BuildMenuSegment($outputDirectory, $isFramed, $entry->GroupContent()); - - my $entryNumber; - - if (!$menuNumbersAndLengthsDone) - { - $entryNumber = $menuGroupNumber; - $menuGroupNumber++; - - $menuGroupLengths{$entry} = $entryLength; - $menuGroupNumbers{$entry} = $entryNumber; - } - else - { $entryNumber = $menuGroupNumbers{$entry}; }; - - $output .= - '<div class=MEntry>' - . '<div class=MGroup>' - - . '<a href="javascript:ToggleMenu(\'MGroupContent' . $entryNumber . '\')"' - . ($isFramed ? ' target="_self"' : '') . '>' - . $self->StringToHTML($entry->Title()) - . '</a>' - - . '<div class=MGroupContent id=MGroupContent' . $entryNumber . '>' - . $entryOutput - . '</div>' - - . '</div>' - . '</div>'; - - $groupLength += MENU_GROUP_LENGTH; - } - - elsif ($entry->Type() == ::MENU_FILE()) - { - my $targetOutputFile = $self->OutputFileOf($entry->Target()); - - # Dependency: BuildMenu() depends on how this formats file entries. - $output .= - '<div class=MEntry>' - . '<div class=MFile>' - . '<a href="' . $self->MakeRelativeURL($outputDirectory, $targetOutputFile) . '">' - . $self->StringToHTML( $entry->Title(), ADD_HIDDEN_BREAKS) - . '</a>' - . '</div>' - . '</div>'; - - $groupLength += MENU_FILE_LENGTH; - } - - elsif ($entry->Type() == ::MENU_TEXT()) - { - $output .= - '<div class=MEntry>' - . '<div class=MText>' - . $self->StringToHTML( $entry->Title() ) - . '</div>' - . '</div>'; - - $groupLength += MENU_TEXT_LENGTH; - } - - elsif ($entry->Type() == ::MENU_LINK()) - { - $output .= - '<div class=MEntry>' - . '<div class=MLink>' - . '<a href="' . $entry->Target() . '"' . ($isFramed ? ' target="_top"' : '') . '>' - . $self->StringToHTML( $entry->Title() ) - . '</a>' - . '</div>' - . '</div>'; - - $groupLength += MENU_LINK_LENGTH; - } - - elsif ($entry->Type() == ::MENU_INDEX()) - { - my $indexFile = $self->IndexFileOf($entry->Target); - - # Dependency: BuildMenu() depends on how this formats index entries. - $output .= - '<div class=MEntry>' - . '<div class=MIndex>' - . '<a href="' . $self->MakeRelativeURL( $outputDirectory, $self->IndexFileOf($entry->Target()) ) . '">' - . $self->StringToHTML( $entry->Title() ) - . '</a>' - . '</div>' - . '</div>'; - - $groupLength += MENU_INDEX_LENGTH; - }; - }; - - - if (!$menuNumbersAndLengthsDone) - { $menuLength += $groupLength; }; - - return ($output, $groupLength); - }; - - -# -# Function: BuildContent -# -# Builds and returns the main page content. -# -# Parameters: -# -# sourceFile - The source <FileName>. -# parsedFile - The parsed source file as an arrayref of <NaturalDocs::Parser::ParsedTopic> objects. -# -# Returns: -# -# The page content in HTML. -# -sub BuildContent #(sourceFile, parsedFile) - { - my ($self, $sourceFile, $parsedFile) = @_; - - $self->ResetToolTips(); - - my $output; - my $i = 0; - - while ($i < scalar @$parsedFile) - { - my $anchor = $self->SymbolToHTMLSymbol($parsedFile->[$i]->Symbol()); - - my $scope = NaturalDocs::Topics->TypeInfo($parsedFile->[$i]->Type())->Scope(); - - - # The anchors are closed, but not around the text, so the :hover CSS style won't accidentally kick in. - - my $headerType; - - if ($i == 0) - { $headerType = 'h1'; } - elsif ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) - { $headerType = 'h2'; } - else - { $headerType = 'h3'; }; - - $output .= - - '<div class=C' . NaturalDocs::Topics->NameOfType($parsedFile->[$i]->Type(), 0, 1) - . ($i == 0 ? ' id=MainTopic' : '') . '>' - - . '<div class=CTopic>' - - . '<' . $headerType . ' class=CTitle>' - . '<a name="' . $anchor . '"></a>' - . $self->StringToHTML( $parsedFile->[$i]->Title(), ADD_HIDDEN_BREAKS) - . '</' . $headerType . '>'; - - - my $hierarchy; - if (NaturalDocs::Topics->TypeInfo( $parsedFile->[$i]->Type() )->ClassHierarchy()) - { - $hierarchy = $self->BuildClassHierarchy($sourceFile, $parsedFile->[$i]->Symbol()); - }; - - my $summary; - if ($i == 0 || $scope == ::SCOPE_START() || $scope == ::SCOPE_END()) - { - $summary .= $self->BuildSummary($sourceFile, $parsedFile, $i); - }; - - my $hasBody; - if (defined $hierarchy || defined $summary || - defined $parsedFile->[$i]->Prototype() || defined $parsedFile->[$i]->Body()) - { - $output .= '<div class=CBody>'; - $hasBody = 1; - }; - - $output .= $hierarchy; - - if (defined $parsedFile->[$i]->Prototype()) - { - $output .= $self->BuildPrototype($parsedFile->[$i]->Type(), $parsedFile->[$i]->Prototype(), $sourceFile); - }; - - if (defined $parsedFile->[$i]->Body()) - { - $output .= $self->NDMarkupToHTML( $sourceFile, $parsedFile->[$i]->Body(), $parsedFile->[$i]->Symbol(), - $parsedFile->[$i]->Package(), $parsedFile->[$i]->Type(), - $parsedFile->[$i]->Using() ); - }; - - $output .= $summary; - - - if ($hasBody) - { $output .= '</div>'; }; - - $output .= - '</div>' # CTopic - . '</div>' # CType - . "\n\n"; - - $i++; - }; - - return $output; - }; - - -# -# Function: BuildSummary -# -# Builds a summary, either for the entire file or the current class/section. -# -# Parameters: -# -# sourceFile - The source <FileName> the summary appears in. -# -# parsedFile - A reference to the parsed source file. -# -# index - The index into the parsed file to start at. If undef or zero, it builds a summary for the entire file. If it's the -# index of a <TopicType> that starts or ends a scope, it builds a summary for that scope -# -# Returns: -# -# The summary in HTML. -# -sub BuildSummary #(sourceFile, parsedFile, index) - { - my ($self, $sourceFile, $parsedFile, $index) = @_; - my $completeSummary; - - if (!defined $index || $index == 0) - { - $index = 0; - $completeSummary = 1; - } - else - { - # Skip the scope entry. - $index++; - }; - - if ($index + 1 >= scalar @$parsedFile) - { return undef; }; - - - my $scope = NaturalDocs::Topics->TypeInfo($parsedFile->[$index]->Type())->Scope(); - - # Return nothing if there's only one entry. - if (!$completeSummary && ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) ) - { return undef; }; - - - my $indent = 0; - my $inGroup; - - # In a nice efficiency twist, these buggers will hold the opening div tags if true, undef if false. Not that this script is known - # for its efficiency. Not that Perl is known for its efficiency. Anyway... - my $isMarkedAttr; - my $entrySizeAttr = ' class=SEntrySize'; - my $descriptionSizeAttr = ' class=SDescriptionSize'; - - my $output = - '<!--START_ND_SUMMARY-->' - . '<div class=Summary><div class=STitle>Summary</div>' - - # Not all browsers get table padding right, so we need a div to apply the border. - . '<div class=SBorder>' - . '<table border=0 cellspacing=0 cellpadding=0 class=STable>'; - - while ($index < scalar @$parsedFile) - { - my $topic = $parsedFile->[$index]; - my $scope = NaturalDocs::Topics->TypeInfo($topic->Type())->Scope(); - - if (!$completeSummary && ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) ) - { last; }; - - - # Remove modifiers as appropriate for the current entry. - - if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) - { - $indent = 0; - $inGroup = 0; - $isMarkedAttr = undef; - } - elsif ($topic->Type() eq ::TOPIC_GROUP()) - { - if ($inGroup) - { $indent--; }; - - $inGroup = 0; - $isMarkedAttr = undef; - }; - - - $output .= - '<tr' . $isMarkedAttr . '><td' . $entrySizeAttr . '>' - . '<div class=S' . ($index == 0 ? 'Main' : NaturalDocs::Topics->NameOfType($topic->Type(), 0, 1)) . '>' - . '<div class=SEntry>'; - - - # Add any remaining modifiers to the HTML in the form of div tags. This modifier approach isn't the most elegant - # thing, but there's not a lot of options. It works. - - if ($indent) - { $output .= '<div class=SIndent' . $indent . '>'; }; - - - # Add the entry itself. - - my $toolTipProperties; - - # We only want a tooltip here if there's a protoype. Otherwise it's redundant. - - if (defined $topic->Prototype()) - { - my $tooltipID = $self->BuildToolTip($topic->Symbol(), $sourceFile, $topic->Type(), - $topic->Prototype(), $topic->Summary()); - $toolTipProperties = $self->BuildToolTipLinkProperties($tooltipID); - }; - - $output .= - '<a href="#' . $self->SymbolToHTMLSymbol($parsedFile->[$index]->Symbol()) . '" ' . $toolTipProperties . '>' - . $self->StringToHTML( $parsedFile->[$index]->Title(), ADD_HIDDEN_BREAKS) - . '</a>'; - - - # Close the modifiers. - - if ($indent) - { $output .= '</div>'; }; - - $output .= - '</div>' # Entry - . '</div>' # Type - - . '</td><td' . $descriptionSizeAttr . '>' - - . '<div class=S' . ($index == 0 ? 'Main' : NaturalDocs::Topics->NameOfType($topic->Type(), 0, 1)) . '>' - . '<div class=SDescription>'; - - - # Add the modifiers to the HTML yet again. - - if ($indent) - { $output .= '<div class=SIndent' . $indent . '>'; }; - - - if (defined $parsedFile->[$index]->Body()) - { - $output .= $self->NDMarkupToHTML($sourceFile, $parsedFile->[$index]->Summary(), - $parsedFile->[$index]->Symbol(), $parsedFile->[$index]->Package(), - $parsedFile->[$index]->Type(), $parsedFile->[$index]->Using()); - }; - - - # Close the modifiers again. - - if ($indent) - { $output .= '</div>'; }; - - - $output .= - '</div>' # Description - . '</div>' # Type - - . '</td></tr>'; - - - # Prepare the modifiers for the next entry. - - if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) - { - $indent = 1; - $inGroup = 0; - } - elsif ($topic->Type() eq ::TOPIC_GROUP()) - { - if (!$inGroup) - { - $indent++; - $inGroup = 1; - }; - }; - - if (!defined $isMarkedAttr) - { $isMarkedAttr = ' class=SMarked'; } - else - { $isMarkedAttr = undef; }; - - $entrySizeAttr = undef; - $descriptionSizeAttr = undef; - - $index++; - }; - - $output .= - '</table>' - . '</div>' # Border - . '</div>' # Summary - . "<!--END_ND_SUMMARY-->"; - - return $output; - }; - - -# -# Function: BuildPrototype -# -# Builds and returns the prototype as HTML. -# -# Parameters: -# -# type - The <TopicType> the prototype is from. -# prototype - The prototype to format. -# file - The <FileName> the prototype was defined in. -# -# Returns: -# -# The prototype in HTML. -# -sub BuildPrototype #(type, prototype, file) - { - my ($self, $type, $prototype, $file) = @_; - - my $language = NaturalDocs::Languages->LanguageOf($file); - my $prototypeObject = $language->ParsePrototype($type, $prototype); - - my $output; - - if ($prototypeObject->OnlyBeforeParameters()) - { - $output = - # A blockquote to scroll it if it's too long. - '<blockquote>' - # A surrounding table as a hack to make the div form-fit. - . '<table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td>' - . $self->ConvertAmpChars($prototypeObject->BeforeParameters()) - . '</td></tr></table>' - . '</blockquote>'; - } - - else - { - my $params = $prototypeObject->Parameters(); - my $beforeParams = $prototypeObject->BeforeParameters(); - my $afterParams = $prototypeObject->AfterParameters(); - - - # Determine what features the prototype has and its length. - - my ($hasType, $hasTypePrefix, $hasNamePrefix, $hasDefaultValue, $hasDefaultValuePrefix); - my $maxParamLength = 0; - - foreach my $param (@$params) - { - my $paramLength = length($param->Name()); - - if ($param->Type()) - { - $hasType = 1; - $paramLength += length($param->Type()) + 1; - }; - if ($param->TypePrefix()) - { - $hasTypePrefix = 1; - $paramLength += length($param->TypePrefix()) + 1; - }; - if ($param->NamePrefix()) - { - $hasNamePrefix = 1; - $paramLength += length($param->NamePrefix()); - }; - if ($param->DefaultValue()) - { - $hasDefaultValue = 1; - - # The length of the default value part is either the longest word, or 1/3 the total, whichever is longer. We do this - # because we don't want parameter lines wrapping to more than three lines, and there's no guarantee that the line will - # wrap at all. There's a small possibility that it could still wrap to four lines with this code, but we don't need to go - # crazy(er) here. - - my $thirdLength = length($param->DefaultValue()) / 3; - - my @words = split(/ +/, $param->DefaultValue()); - my $maxWordLength = 0; - - foreach my $word (@words) - { - if (length($word) > $maxWordLength) - { $maxWordLength = length($word); }; - }; - - $paramLength += ($maxWordLength > $thirdLength ? $maxWordLength : $thirdLength) + 1; - }; - if ($param->DefaultValuePrefix()) - { - $hasDefaultValuePrefix = 1; - $paramLength += length($param->DefaultValuePrefix()) + 1; - }; - - if ($paramLength > $maxParamLength) - { $maxParamLength = $paramLength; }; - }; - - my $useCondensed = (length($beforeParams) + $maxParamLength + length($afterParams) > 80 ? 1 : 0); - my $parameterColumns = 1 + $hasType + $hasTypePrefix + $hasNamePrefix + - $hasDefaultValue + $hasDefaultValuePrefix + $useCondensed; - - $output = - '<blockquote><table border=0 cellspacing=0 cellpadding=0 class=Prototype><tr><td>' - - # Stupid hack to get it to work right in IE. - . '<table border=0 cellspacing=0 cellpadding=0><tr>' - - . '<td class=PBeforeParameters ' . ($useCondensed ? 'colspan=' . $parameterColumns : 'nowrap') . '>' - . $self->ConvertAmpChars($beforeParams); - - if ($beforeParams && $beforeParams !~ /[\(\[\{\<]$/) - { $output .= ' '; }; - - $output .= - '</td>'; - - for (my $i = 0; $i < scalar @$params; $i++) - { - if ($useCondensed) - { - $output .= '</tr><tr><td> </td>'; - } - elsif ($i > 0) - { - # Go to the next row and and skip the BeforeParameters cell. - $output .= '</tr><tr><td></td>'; - }; - - if ($language->TypeBeforeParameter()) - { - if ($hasTypePrefix) - { - my $htmlTypePrefix = $self->ConvertAmpChars($params->[$i]->TypePrefix()); - $htmlTypePrefix =~ s/ $/ /; - - $output .= - '<td class=PTypePrefix nowrap>' - . $htmlTypePrefix - . '</td>'; - }; - - if ($hasType) - { - $output .= - '<td class=PType nowrap>' - . $self->ConvertAmpChars($params->[$i]->Type()) . ' ' - . '</td>'; - }; - - if ($hasNamePrefix) - { - $output .= - '<td class=PParameterPrefix nowrap>' - . $self->ConvertAmpChars($params->[$i]->NamePrefix()) - . '</td>'; - }; - - $output .= - '<td class=PParameter nowrap' . ($useCondensed && !$hasDefaultValue ? ' width=100%' : '') . '>' - . $self->ConvertAmpChars($params->[$i]->Name()) - . '</td>'; - } - - else # !$language->TypeBeforeParameter() - { - $output .= - '<td class=PParameter nowrap>' - . $self->ConvertAmpChars( $params->[$i]->NamePrefix() . $params->[$i]->Name() ) - . '</td>'; - - if ($hasType || $hasTypePrefix) - { - my $typePrefix = $params->[$i]->TypePrefix(); - if ($typePrefix) - { $typePrefix .= ' '; }; - - $output .= - '<td class=PType nowrap' . ($useCondensed && !$hasDefaultValue ? ' width=100%' : '') . '>' - . ' ' . $self->ConvertAmpChars( $typePrefix . $params->[$i]->Type() ) - . '</td>'; - }; - }; - - if ($hasDefaultValuePrefix) - { - $output .= - '<td class=PDefaultValuePrefix>' - . ' ' . $self->ConvertAmpChars( $params->[$i]->DefaultValuePrefix() ) . ' ' - . '</td>'; - }; - - if ($hasDefaultValue) - { - $output .= - '<td class=PDefaultValue width=100%>' - . ($hasDefaultValuePrefix ? '' : ' ') . $self->ConvertAmpChars( $params->[$i]->DefaultValue() ) - . '</td>'; - }; - }; - - if ($useCondensed) - { $output .= '</tr><tr>'; }; - - $output .= - '<td class=PAfterParameters ' . ($useCondensed ? 'colspan=' . $parameterColumns : 'nowrap') . '>' - . $self->ConvertAmpChars($afterParams); - - if ($afterParams && $afterParams !~ /^[\)\]\}\>]/) - { $output .= ' '; }; - - $output .= - '</td>' - . '</tr></table>' - - # Hack. - . '</td></tr></table></blockquote>'; - }; - - return $output; - }; - - -# -# Function: BuildFooter -# -# Builds and returns the HTML footer for the page. -# -sub BuildFooter - { - my $self = shift; - my $footer = NaturalDocs::Menu->Footer(); - - if (defined $footer) - { - if (substr($footer, -1, 1) ne '.') - { $footer .= '.'; }; - - $footer =~ s/\(c\)/©/gi; - $footer =~ s/\(tm\)/™/gi; - $footer =~ s/\(r\)/®/gi; - - $footer .= ' Generated by <a href="' . NaturalDocs::Settings->AppURL() . '">Natural Docs</a>.' - } - else - { - $footer = 'Generated by <a href="' . NaturalDocs::Settings->AppURL() . '">Natural Docs</a>'; - }; - - return '<!--START_ND_FOOTER-->' . $footer . '<!--END_ND_FOOTER-->'; - }; - - -# -# Function: BuildToolTip -# -# Builds the HTML for a symbol's tooltip and stores it in <tooltipHTML>. -# -# Parameters: -# -# symbol - The target <SymbolString>. -# file - The <FileName> the target's defined in. -# type - The symbol <TopicType>. -# prototype - The target prototype, or undef for none. -# summary - The target summary, or undef for none. -# -# Returns: -# -# If a tooltip is necessary for the link, returns the tooltip ID. If not, returns undef. -# -sub BuildToolTip #(symbol, file, type, prototype, summary) - { - my ($self, $symbol, $file, $type, $prototype, $summary) = @_; - - if (defined $prototype || defined $summary) - { - my $htmlSymbol = $self->SymbolToHTMLSymbol($symbol); - my $number = $tooltipSymbolsToNumbers{$htmlSymbol}; - - if (!defined $number) - { - $number = $tooltipNumber; - $tooltipNumber++; - - $tooltipSymbolsToNumbers{$htmlSymbol} = $number; - - $tooltipHTML .= - '<div class=CToolTip id="tt' . $number . '">' - . '<div class=C' . NaturalDocs::Topics->NameOfType($type, 0, 1) . '>'; - - if (defined $prototype) - { - $tooltipHTML .= $self->BuildPrototype($type, $prototype, $file); - }; - - if (defined $summary) - { - # Remove links, since people can't/shouldn't be clicking on tooltips anyway. - $summary =~ s/<\/?(?:link|url)>//g; - - # The fact that we don't have scope or using shouldn't matter because we removed the links. - $summary = $self->NDMarkupToHTML($file, $summary, undef, undef, $type, undef); - - # XXX - Hack. We want to remove e-mail links as well, but keep their obfuscation. So we leave the tags in there for - # the NDMarkupToHTML call, then strip out the link part afterwards. The text obfuscation should still be in place. - - $summary =~ s/<\/?a[^>]+>//g; - - $tooltipHTML .= $summary; - }; - - $tooltipHTML .= - '</div>' - . '</div>'; - }; - - return 'tt' . $number; - } - else - { return undef; }; - }; - -# -# Function: BuildToolTips -# -# Builds and returns the tooltips for the page in HTML. -# -sub BuildToolTips - { - my $self = shift; - return "\n<!--START_ND_TOOLTIPS-->\n" . $tooltipHTML . "<!--END_ND_TOOLTIPS-->\n\n"; - }; - -# -# Function: BuildClassHierarchy -# -# Builds and returns a class hierarchy diagram for the passed class, if applicable. -# -# Parameters: -# -# file - The source <FileName>. -# class - The class <SymbolString> to build the hierarchy of. -# -sub BuildClassHierarchy #(file, symbol) - { - my ($self, $file, $symbol) = @_; - - my @parents = NaturalDocs::ClassHierarchy->ParentsOf($symbol); - @parents = sort { ::StringCompare($a, $b) } @parents; - - my @children = NaturalDocs::ClassHierarchy->ChildrenOf($symbol); - @children = sort { ::StringCompare($a, $b) } @children; - - if (!scalar @parents && !scalar @children) - { return undef; }; - - my $output = - '<div class=ClassHierarchy>'; - - if (scalar @parents) - { - $output .='<table border=0 cellspacing=0 cellpadding=0><tr><td>'; - - foreach my $parent (@parents) - { - $output .= $self->BuildClassHierarchyEntry($file, $parent, 'CHParent', 1); - }; - - $output .= '</td></tr></table><div class=CHIndent>'; - }; - - $output .= - '<table border=0 cellspacing=0 cellpadding=0><tr><td>' - . $self->BuildClassHierarchyEntry($file, $symbol, 'CHCurrent', undef) - . '</td></tr></table>'; - - if (scalar @children) - { - $output .= - '<div class=CHIndent>' - . '<table border=0 cellspacing=0 cellpadding=0><tr><td>'; - - if (scalar @children <= 5) - { - for (my $i = 0; $i < scalar @children; $i++) - { $output .= $self->BuildClassHierarchyEntry($file, $children[$i], 'CHChild', 1); }; - } - else - { - for (my $i = 0; $i < 4; $i++) - { $output .= $self->BuildClassHierarchyEntry($file, $children[$i], 'CHChild', 1); }; - - $output .= '<div class=CHChildNote><div class=CHEntry>' . (scalar @children - 4) . ' other children</div></div>'; - }; - - $output .= - '</td></tr></table>' - . '</div>'; - }; - - if (scalar @parents) - { $output .= '</div>'; }; - - $output .= - '</div>'; - - return $output; - }; - - -# -# Function: BuildClassHierarchyEntry -# -# Builds and returns a single class hierarchy entry. -# -# Parameters: -# -# file - The source <FileName>. -# symbol - The class <SymbolString> whose entry is getting built. -# style - The style to apply to the entry, such as <CHParent>. -# link - Whether to build a link for this class or not. When building the selected class' entry, this should be false. It will -# automatically handle whether the symbol is defined or not. -# -sub BuildClassHierarchyEntry #(file, symbol, style, link) - { - my ($self, $file, $symbol, $style, $link) = @_; - - my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol); - my $name = join(NaturalDocs::Languages->LanguageOf($file)->PackageSeparator(), @identifiers); - $name = $self->StringToHTML($name); - - my $output = '<div class=' . $style . '><div class=CHEntry>'; - - if ($link) - { - my $target = NaturalDocs::SymbolTable->Lookup($symbol, $file); - - if (defined $target) - { - my $targetFile; - - if ($target->File() ne $file) - { $targetFile = $self->MakeRelativeURL( $self->OutputFileOf($file), $self->OutputFileOf($target->File()), 1 ); }; - # else leave it undef - - my $targetTooltipID = $self->BuildToolTip($symbol, $target->File(), $target->Type(), - $target->Prototype(), $target->Summary()); - - my $toolTipProperties = $self->BuildToolTipLinkProperties($targetTooltipID); - - $output .= '<a href="' . $targetFile . '#' . $self->SymbolToHTMLSymbol($symbol) . '" ' - . 'class=L' . NaturalDocs::Topics->NameOfType($target->Type(), 0, 1) . ' ' . $toolTipProperties . '>' - . $name . '</a>'; - } - else - { $output .= $name; }; - } - else - { $output .= $name; }; - - $output .= '</div></div>'; - return $output; - }; - - -# -# Function: OpeningBrowserStyles -# -# Returns the JavaScript that will add opening browser styles if necessary. -# -sub OpeningBrowserStyles - { - my $self = shift; - - return - - '<script language=JavaScript><!--' . "\n" - - # IE 4 and 5 don't understand 'undefined', so you can't say '!= undefined'. - . 'if (browserType) {' - . 'document.write("<div class=" + browserType + ">");' - . 'if (browserVer) {' - . 'document.write("<div class=" + browserVer + ">"); }' - . '}' - - . '// --></script>'; - }; - - -# -# Function: ClosingBrowserStyles -# -# Returns the JavaScript that will close browser styles if necessary. -# -sub ClosingBrowserStyles - { - my $self = shift; - - return - - '<script language=JavaScript><!--' . "\n" - - . 'if (browserType) {' - . 'if (browserVer) {' - . 'document.write("</div>"); }' - . 'document.write("</div>");' - . '}' - - . '// --></script>'; - }; - - -# -# Function: StandardComments -# -# Returns the standard HTML comments that should be included in every generated file. This includes <IEWebMark()>, so this -# really is required for proper functionality. -# -sub StandardComments - { - my $self = shift; - - return "\n\n" - - . '<!-- Generated by Natural Docs, version ' . NaturalDocs::Settings->TextAppVersion() . ' -->' . "\n" - . '<!-- ' . NaturalDocs::Settings->AppURL() . ' -->' . "\n\n" - . $self->IEWebMark() . "\n\n"; - }; - - -# -# Function: IEWebMark -# -# Returns the HTML comment necessary to get around the security warnings in IE starting with Windows XP Service Pack 2. -# -# With this mark, the HTML page is treated as if it were in the Internet security zone instead of the Local Machine zone. This -# prevents the lockdown on scripting that causes an error message to appear with each page. -# -# More Information: -# -# - http://www.microsoft.com/technet/prodtechnol/winxppro/maintain/sp2brows.mspx#EHAA -# - http://www.phdcc.com/xpsp2.htm#markoftheweb -# -sub IEWebMark - { - my $self = shift; - - return '<!-- saved from url=(0026)http://www.naturaldocs.org -->'; - }; - - - -############################################################################### -# Group: Index Functions - - -# -# Function: BuildIndexPages -# -# Builds an index file or files. -# -# Parameters: -# -# type - The <TopicType> the index is limited to, or undef for none. -# index - An arrayref of sections, each section being an arrayref <NaturalDocs::SymbolTable::IndexElement> objects. -# The first section is for symbols, the second for numbers, and the rest for A through Z. -# beginPage - All the content of the HTML page up to where the index content should appear. -# endPage - All the content of the HTML page past where the index should appear. -# -# Returns: -# -# The number of pages in the index. -# -sub BuildIndexPages #(type, index, beginPage, endPage) - { - my ($self, $type, $indexSections, $beginPage, $endPage) = @_; - - # Build the content. - - my ($indexHTMLSections, $tooltipHTMLSections) = $self->BuildIndexSections($indexSections, $self->IndexFileOf($type, 1)); - - - my $page = 1; - my $pageSize = 0; - my @pageLocations; - - # The maximum page size acceptable before starting a new page. Note that this doesn't include beginPage and endPage, - # because we don't want something like a large menu screwing up the calculations. - use constant PAGESIZE_LIMIT => 50000; - - - # File the pages. - - for (my $i = 0; $i < scalar @$indexHTMLSections; $i++) - { - if (!defined $indexHTMLSections->[$i]) - { next; }; - - $pageSize += length($indexHTMLSections->[$i]) + length($tooltipHTMLSections->[$i]); - $pageLocations[$i] = $page; - - if ($pageSize + length($indexHTMLSections->[$i+1]) + length($tooltipHTMLSections->[$i+1]) > PAGESIZE_LIMIT) - { - $page++; - $pageSize = 0; - }; - }; - - - # Build the pages. - - my $indexFileName; - $page = -1; - my $oldPage = -1; - my $tooltips; - my $firstHeading; - - for (my $i = 0; $i < scalar @$indexHTMLSections; $i++) - { - if (!defined $indexHTMLSections->[$i]) - { next; }; - - $page = $pageLocations[$i]; - - # Switch files if we need to. - - if ($page != $oldPage) - { - if ($oldPage != -1) - { - print INDEXFILEHANDLE '</table>' . $tooltips . $endPage; - close(INDEXFILEHANDLE); - $tooltips = undef; - }; - - $indexFileName = $self->IndexFileOf($type, $page); - - open(INDEXFILEHANDLE, '>' . $indexFileName) - or die "Couldn't create output file " . $indexFileName . ".\n"; - - print INDEXFILEHANDLE $beginPage . $self->BuildIndexNavigationBar($type, $page, \@pageLocations) - . '<table border=0 cellspacing=0 cellpadding=0>'; - - $oldPage = $page; - $firstHeading = 1; - }; - - print INDEXFILEHANDLE - '<tr>' - . '<td class=IHeading' . ($firstHeading ? ' id=IFirstHeading' : '') . '>' - . '<a name="' . $indexAnchors[$i] . '"></a>' - . $indexHeadings[$i] - . '</td>' - . '<td></td>' - . '</tr>' - - . $indexHTMLSections->[$i]; - - $firstHeading = 0; - $tooltips .= $tooltipHTMLSections->[$i]; - }; - - if ($page != -1) - { - print INDEXFILEHANDLE '</table>' . $tooltips . $endPage; - close(INDEXFILEHANDLE); - } - - # Build a dummy page so there's something at least. - else - { - $indexFileName = $self->IndexFileOf($type, 1); - - open(INDEXFILEHANDLE, '>' . $indexFileName) - or die "Couldn't create output file " . $indexFileName . ".\n"; - - print INDEXFILEHANDLE - $beginPage - . $self->BuildIndexNavigationBar($type, 1, \@pageLocations) - . 'There are no entries in the ' . lc( NaturalDocs::Topics->NameOfType($type) ) . ' index.' - . $endPage; - - close(INDEXFILEHANDLE); - }; - - - return $page; - }; - - -# -# Function: BuildIndexSections -# -# Builds and returns index's sections in HTML. -# -# Parameters: -# -# index - An arrayref of sections, each section being an arrayref <NaturalDocs::SymbolTable::IndexElement> objects. -# The first section is for symbols, the second for numbers, and the rest for A through Z. -# outputFile - The output file the index is going to be stored in. Since there may be multiple files, just send the first file. The -# path is what matters, not the file name. -# -# Returns: -# -# The arrayref ( indexSections, tooltipSections ). -# -# Index 0 is the symbols, index 1 is the numbers, and each following index is A through Z. The content of each section -# is its HTML, or undef if there is nothing for that section. -# -sub BuildIndexSections #(index, outputFile) - { - my ($self, $indexSections, $outputFile) = @_; - - $self->ResetToolTips(); - - my $contentSections = [ ]; - my $tooltipSections = [ ]; - - for (my $section = 0; $section < scalar @$indexSections; $section++) - { - if (defined $indexSections->[$section]) - { - my $total = scalar @{$indexSections->[$section]}; - - for (my $i = 0; $i < $total; $i++) - { - my $id; - - if ($i == 0) - { - if ($total == 1) - { $id = 'IOnlySymbolPrefix'; } - else - { $id = 'IFirstSymbolPrefix'; }; - } - elsif ($i == $total - 1) - { $id = 'ILastSymbolPrefix'; }; - - $contentSections->[$section] .= $self->BuildIndexElement($indexSections->[$section]->[$i], $outputFile, $id); - }; - - $tooltipSections->[$section] .= $self->BuildToolTips(); - $self->ResetToolTips(1); - }; - }; - - - return ( $contentSections, $tooltipSections ); - }; - - -# -# Function: BuildIndexElement -# -# Converts a <NaturalDocs::SymbolTable::IndexElement> to HTML and returns it. It will handle all sub-elements automatically. -# -# Parameters: -# -# element - The <NaturalDocs::SymbolTable::IndexElement> to build. -# outputFile - The output <FileName> this is appearing in. -# id - The CSS ID to apply to the prefix. -# -# Recursion-Only Parameters: -# -# These parameters are used internally for recursion, and should not be set. -# -# symbol - If the element is below symbol level, the <SymbolString> to use. -# package - If the element is below package level, the package <SymbolString> to use. -# hasPackage - Whether the element is below package level. Is necessary because package may need to be undef. -# -sub BuildIndexElement #(element, outputFile, id, symbol, package, hasPackage) - { - my ($self, $element, $outputFile, $id, $symbol, $package, $hasPackage) = @_; - - my $output; - - - # If we're doing a file sub-index entry... - - if ($hasPackage) - { - my ($inputDirectory, $relativePath) = NaturalDocs::Settings->SplitFromInputDirectory($element->File()); - - $output = - $self->BuildIndexLink($self->StringToHTML($relativePath, ADD_HIDDEN_BREAKS), $symbol, - $package, $element->File(), $element->Type(), $element->Prototype(), - $element->Summary(), $outputFile, 'IFile') - } - - - # If we're doing a package sub-index entry... - - elsif (defined $symbol) - { - my $text; - - if ($element->Package()) - { - $text = NaturalDocs::SymbolString->ToText($element->Package(), $element->PackageSeparator()); - $text = $self->StringToHTML($text, ADD_HIDDEN_BREAKS); - } - else - { $text = 'Global'; }; - - if (!$element->HasMultipleFiles()) - { - $output .= $self->BuildIndexLink($text, $symbol, $element->Package(), $element->File(), $element->Type(), - $element->Prototype(), $element->Summary(), $outputFile, 'IParent'); - } - - else - { - $output .= - '<span class=IParent>' - . $text - . '</span>' - . '<div class=ISubIndex>'; - - my $fileElements = $element->File(); - foreach my $fileElement (@$fileElements) - { - $output .= $self->BuildIndexElement($fileElement, $outputFile, $id, $symbol, $element->Package(), 1); - }; - - $output .= - '</div>'; - }; - } - - - # If we're doing a top-level symbol entry... - - else - { - my $symbolText = $self->StringToHTML($element->SortableSymbol(), ADD_HIDDEN_BREAKS); - my $symbolPrefix = $self->StringToHTML($element->IgnoredPrefix()); - - $output .= - '<tr>' - . '<td class=ISymbolPrefix' . ($id ? ' id=' . $id : '') . '>' - . ($symbolPrefix || ' ') - . '</td><td class=IEntry>'; - - if (!$element->HasMultiplePackages()) - { - my $packageText; - - if (defined $element->Package()) - { - $packageText = NaturalDocs::SymbolString->ToText($element->Package(), $element->PackageSeparator()); - $packageText = $self->StringToHTML($packageText, ADD_HIDDEN_BREAKS); - }; - - if (!$element->HasMultipleFiles()) - { - $output .= - $self->BuildIndexLink($symbolText, $element->Symbol(), $element->Package(), $element->File(), - $element->Type(), $element->Prototype(), $element->Summary(), $outputFile, 'ISymbol'); - - if (defined $packageText) - { - $output .= - ', <span class=IParent>' - . $packageText - . '</span>'; - }; - } - else # hasMultipleFiles but not mulitplePackages - { - $output .= - '<span class=ISymbol>' - . $symbolText - . '</span>'; - - if (defined $packageText) - { - $output .= - ', <span class=IParent>' - . $packageText - . '</span>'; - }; - - $output .= - '<div class=ISubIndex>'; - - my $fileElements = $element->File(); - foreach my $fileElement (@$fileElements) - { - $output .= $self->BuildIndexElement($fileElement, $outputFile, $id, $element->Symbol(), $element->Package(), 1); - }; - - $output .= - '</div>'; - }; - } - - else # hasMultiplePackages - { - $output .= - '<span class=ISymbol>' - . $symbolText - . '</span>' - . '<div class=ISubIndex>'; - - my $packageElements = $element->Package(); - foreach my $packageElement (@$packageElements) - { - $output .= $self->BuildIndexElement($packageElement, $outputFile, $id, $element->Symbol()); - }; - - $output .= - '</div>'; - }; - - $output .= '</td></tr>'; - }; - - - return $output; - }; - - -# -# Function: BuildIndexLink -# -# Builds and returns the HTML associated with an index link. The HTML will be the a href tag, the text, and the closing tag. -# -# Parameters: -# -# text - The text of the link *in HTML*. Use <IndexSymbolToHTML()> if necessary. -# symbol - The partial <SymbolString> to link to. -# package - The package <SymbolString> of the symbol. -# file - The <FileName> the symbol is defined in. -# type - The <TopicType> of the symbol. -# prototype - The prototype of the symbol, or undef if none. -# summary - The summary of the symbol, or undef if none. -# outputFile - The HTML <FileName> this link will appear in. -# style - The CSS style to apply to the link. -# -sub BuildIndexLink #(text, symbol, package, file, type, prototype, summary, outputFile, style) - { - my ($self, $text, $symbol, $package, $file, $type, $prototype, $summary, $outputFile, $style) = @_; - - $symbol = NaturalDocs::SymbolString->Join($package, $symbol); - - my $targetTooltipID = $self->BuildToolTip($symbol, $file, $type, $prototype, $summary); - my $toolTipProperties = $self->BuildToolTipLinkProperties($targetTooltipID); - - return '<a href="' . $self->MakeRelativeURL( $outputFile, $self->OutputFileOf($file), 1 ) - . '#' . $self->SymbolToHTMLSymbol($symbol) . '" ' . $toolTipProperties . ' ' - . 'class=' . $style . '>' . $text . '</a>'; - }; - - -# -# Function: BuildIndexNavigationBar -# -# Builds a navigation bar for a page of the index. -# -# Parameters: -# -# type - The <TopicType> of the index, or undef for general. -# page - The page of the index the navigation bar is for. -# locations - An arrayref of the locations of each section. Index 0 is for the symbols, index 1 for the numbers, and the rest -# for each letter. The values are the page numbers where the sections are located. -# -sub BuildIndexNavigationBar #(type, page, locations) - { - my ($self, $type, $page, $locations) = @_; - - my $output = '<div class=INavigationBar>'; - - for (my $i = 0; $i < scalar @indexHeadings; $i++) - { - if ($i != 0) - { $output .= ' · '; }; - - if (defined $locations->[$i]) - { - $output .= '<a href="'; - - if ($locations->[$i] != $page) - { $output .= $self->RelativeIndexFileOf($type, $locations->[$i]); }; - - $output .= '#' . $indexAnchors[$i] . '">' . $indexHeadings[$i] . '</a>'; - } - else - { - $output .= $indexHeadings[$i]; - }; - }; - - $output .= '</div>'; - - return $output; - }; - - - -############################################################################### -# Group: File Functions - - -# -# function: PurgeIndexFiles -# -# Removes all or some of the output files for an index. -# -# Parameters: -# -# type - The index <TopicType>. -# startingPage - If defined, only pages starting with this number will be removed. Otherwise all pages will be removed. -# -sub PurgeIndexFiles #(type, startingPage) - { - my ($self, $type, $page) = @_; - - if (!defined $page) - { $page = 1; }; - - for (;;) - { - my $file = $self->IndexFileOf($type, $page); - - if (-e $file) - { - unlink($file); - $page++; - } - else - { - last; - }; - }; - }; - - -# -# function: OutputFileOf -# -# Returns the output file name of the source file. Will be undef if it is not a file from a valid input directory. -# -sub OutputFileOf #(sourceFile) - { - my ($self, $sourceFile) = @_; - - my ($inputDirectory, $relativeSourceFile) = NaturalDocs::Settings->SplitFromInputDirectory($sourceFile); - if (!defined $inputDirectory) - { return undef; }; - - my $outputDirectory = NaturalDocs::Settings->OutputDirectoryOf($self); - my $inputDirectoryName = NaturalDocs::Settings->InputDirectoryNameOf($inputDirectory); - - $outputDirectory = NaturalDocs::File->JoinPaths( $outputDirectory, - 'files' . ($inputDirectoryName != 1 ? $inputDirectoryName : ''), 1 ); - - # We need to change any extensions to dashes because Apache will think file.pl.html is a script. - # We also need to add a dash if the file doesn't have an extension so there'd be no conflicts with index.html, - # FunctionIndex.html, etc. - - if (!($relativeSourceFile =~ tr/./-/)) - { $relativeSourceFile .= '-'; }; - - $relativeSourceFile =~ tr/ /_/; - $relativeSourceFile .= '.html'; - - return NaturalDocs::File->JoinPaths($outputDirectory, $relativeSourceFile); - }; - - -# -# Function: IndexDirectory -# -# Returns the directory of the index files. -# -sub IndexDirectory - { - my $self = shift; - return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'index', 1); - }; - - -# -# function: IndexFileOf -# -# Returns the output file name of the index file. -# -# Parameters: -# -# type - The <TopicType> of the index. -# page - The page number. Undef is the same as one. -# -sub IndexFileOf #(type, page) - { - my ($self, $type, $page) = @_; - return NaturalDocs::File->JoinPaths( $self->IndexDirectory(), $self->RelativeIndexFileOf($type, $page) ); - }; - -# -# function: RelativeIndexFileOf -# -# Returns the output file name of the index file, relative to other index files. -# -# Parameters: -# -# type - The <TopicType> of the index. -# page - The page number. Undef is the same as one. -# -sub RelativeIndexFileOf #(type, page) - { - my ($self, $type, $page) = @_; - return NaturalDocs::Topics->NameOfType($type, 1, 1) . (defined $page && $page != 1 ? $page : '') . '.html'; - }; - - -# -# function: CSSDirectory -# -# Returns the directory of the CSS files. -# -sub CSSDirectory - { - my $self = shift; - return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'styles', 1); - }; - - -# -# Function: MainCSSFile -# -# Returns the location of the main CSS file. -# -sub MainCSSFile - { - my $self = shift; - return NaturalDocs::File->JoinPaths( $self->CSSDirectory(), 'main.css' ); - }; - - -# -# function: JavaScriptDirectory -# -# Returns the directory of the JavaScript files. -# -sub JavaScriptDirectory - { - my $self = shift; - return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->OutputDirectoryOf($self), 'javascript', 1); - }; - - -# -# Function: MainJavaScriptFile -# -# Returns the location of the main JavaScript file. -# -sub MainJavaScriptFile - { - my $self = shift; - return NaturalDocs::File->JoinPaths( $self->JavaScriptDirectory(), 'main.js' ); - }; - - - - -############################################################################### -# Group: Support Functions - - -# -# function:IndexTitleOf -# -# Returns the page title of the index file. -# -# Parameters: -# -# type - The type of index. -# -sub IndexTitleOf #(type) - { - my ($self, $type) = @_; - - return ($type eq ::TOPIC_GENERAL() ? '' : NaturalDocs::Topics->NameOfType($type) . ' ') . 'Index'; - }; - -# -# function: MakeRelativeURL -# -# Returns a relative path between two files in the output tree and returns it in URL format. -# -# Parameters: -# -# baseFile - The base <FileName> in local format, *not* in URL format. -# targetFile - The target <FileName> of the link in local format, *not* in URL format. -# baseHasFileName - Whether baseFile has a file name attached or is just a path. -# -# Returns: -# -# The relative URL to the target. -# -sub MakeRelativeURL #(FileName baseFile, FileName targetFile, bool baseHasFileName) -> string relativeURL - { - my ($self, $baseFile, $targetFile, $baseHasFileName) = @_; - - if ($baseHasFileName) - { $baseFile = NaturalDocs::File->NoFileName($baseFile) }; - - my $relativePath = NaturalDocs::File->MakeRelativePath($baseFile, $targetFile); - - return $self->ConvertAmpChars( NaturalDocs::File->ConvertToURL($relativePath) ); - }; - -# -# Function: StringToHTML -# -# Converts a text string to HTML. Does not apply paragraph tags or accept formatting tags. -# -# Parameters: -# -# string - The string to convert. -# addHiddenBreaks - Whether to add hidden breaks to the string. You can use <ADD_HIDDEN_BREAKS> for this parameter -# if you want to make the calling code clearer. -# -# Returns: -# -# The string in HTML. -# -sub StringToHTML #(string, addHiddenBreaks) - { - my ($self, $string, $addHiddenBreaks) = @_; - - $string =~ s/&/&/g; - $string =~ s/</</g; - $string =~ s/>/>/g; - - # Me likey the fancy quotes. They work in IE 4+, Mozilla, and Opera 5+. We've already abandoned NS4 with the CSS - # styles, so might as well. - $string =~ s/^\'/‘/gm; - $string =~ s/([\ \(\[\{])\'/$1‘/g; - $string =~ s/\'/’/g; - - $string =~ s/^\"/“/gm; - $string =~ s/([\ \(\[\{])\"/$1“/g; - $string =~ s/\"/”/g; - - # Me likey the double spaces too. As you can probably tell, I like print-formatting better than web-formatting. The indented - # paragraphs without blank lines in between them do become readable when you have fancy quotes and double spaces too. - $string = $self->AddDoubleSpaces($string); - - if ($addHiddenBreaks) - { $string = $self->AddHiddenBreaks($string); }; - - return $string; - }; - - -# -# Function: SymbolToHTMLSymbol -# -# Converts a <SymbolString> to a HTML symbol, meaning one that is safe to include in anchor and link tags. You don't need -# to pass the result to <ConvertAmpChars()>. -# -sub SymbolToHTMLSymbol #(symbol) - { - my ($self, $symbol) = @_; - - my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol); - my $htmlSymbol = join('.', @identifiers); - - # If only Mozilla was nice about putting special characters in URLs like IE and Opera are, I could leave spaces in and replace - # "<>& with their amp chars. But alas, Mozilla shows them as %20, etc. instead. It would have made for nice looking URLs. - $htmlSymbol =~ tr/ \"<>\?&%/_/d; - - return $htmlSymbol; - }; - - -# -# Function: NDMarkupToHTML -# -# Converts a block of <NDMarkup> to HTML. -# -# Parameters: -# -# sourceFile - The source <FileName> the <NDMarkup> appears in. -# text - The <NDMarkup> text to convert. -# symbol - The topic <SymbolString> the <NDMarkup> appears in. -# package - The package <SymbolString> the <NDMarkup> appears in. -# type - The <TopicType> the <NDMarkup> appears in. -# using - An arrayref of scope <SymbolStrings> the <NDMarkup> also has access to, or undef if none. -# -# Returns: -# -# The text in HTML. -# -sub NDMarkupToHTML #(sourceFile, text, symbol, package, type, using) - { - my ($self, $sourceFile, $text, $symbol, $package, $type, $using) = @_; - - my $dlSymbolBehavior; - - if ($type == ::TOPIC_ENUMERATION()) - { $dlSymbolBehavior = NaturalDocs::Languages->LanguageOf($sourceFile)->EnumValues(); } - elsif (NaturalDocs::Topics->TypeInfo($type)->Scope() == ::SCOPE_ALWAYS_GLOBAL()) - { $dlSymbolBehavior = ::ENUM_GLOBAL(); } - else - { $dlSymbolBehavior = ::ENUM_UNDER_PARENT(); }; - - my $output; - my $inCode; - - my @splitText = split(/(<\/?code>)/, $text); - - while (scalar @splitText) - { - $text = shift @splitText; - - if ($text eq '<code>') - { - $output .= '<blockquote><pre class=CCode>'; - $inCode = 1; - } - elsif ($text eq '</code>') - { - $output .= '</pre></blockquote>'; - $inCode = undef; - } - elsif ($inCode) - { - $text =~ s/\n/<br>/g; - $output .= $text; - } - else - { - # Format non-code text. - - # Convert quotes to fancy quotes. - $text =~ s/^\'/‘/gm; - $text =~ s/([\ \(\[\{])\'/$1‘/g; - $text =~ s/\'/’/g; - - $text =~ s/^"/“/gm; - $text =~ s/([\ \(\[\{])"/$1“/g; - $text =~ s/"/”/g; - - # Copyright symbols. Prevent conversion when part of (a), (b), (c) lists. - if ($text !~ /\(a\)/i) - { $text =~ s/\(c\)/©/gi; }; - - # Trademark symbols. - $text =~ s/\(tm\)/™/gi; - $text =~ s/\(r\)/®/gi; - - # Resolve and convert links. - $text =~ s/<link>([^<]+)<\/link>/$self->BuildTextLink($1, $package, $using, $sourceFile)/ge; - $text =~ s/<url>([^<]+)<\/url>/$self->BuildURLLink($1)/ge; - $text =~ s/<email>([^<]+)<\/email>/$self->BuildEMailLink($1)/eg; - - # Add double spaces too. - $text = $self->AddDoubleSpaces($text); - - # Paragraphs - $text =~ s/<p>/<p class=CParagraph>/g; - - # Bulleted lists - $text =~ s/<ul>/<ul class=CBulletList>/g; - - # Headings - $text =~ s/<h>/<h4 class=CHeading>/g; - $text =~ s/<\/h>/<\/h4>/g; - - # Description Lists - $text =~ s/<dl>/<table border=0 cellspacing=0 cellpadding=0 class=CDescriptionList>/g; - $text =~ s/<\/dl>/<\/table>/g; - - $text =~ s/<de>/<tr><td class=CDLEntry>/g; - $text =~ s/<\/de>/<\/td>/g; - - if ($dlSymbolBehavior == ::ENUM_GLOBAL()) - { $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol(undef, $1)/ge; } - elsif ($dlSymbolBehavior == ::ENUM_UNDER_PARENT()) - { $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol($package, $1)/ge; } - else # ($dlSymbolBehavior == ::ENUM_UNDER_TYPE()) - { $text =~ s/<ds>([^<]+)<\/ds>/$self->MakeDescriptionListSymbol($symbol, $1)/ge; } - - sub MakeDescriptionListSymbol #(package, text) - { - my ($self, $package, $text) = @_; - - $text = NaturalDocs::NDMarkup->RestoreAmpChars($text); - my $symbol = NaturalDocs::SymbolString->FromText($text); - - if (defined $package) - { $symbol = NaturalDocs::SymbolString->Join($package, $symbol); }; - - return - '<tr>' - . '<td class=CDLEntry>' - # The anchors are closed, but not around the text, to prevent the :hover CSS style from kicking in. - . '<a name="' . $self->SymbolToHTMLSymbol($symbol) . '"></a>' - . $text - . '</td>'; - }; - - $text =~ s/<dd>/<td class=CDLDescription>/g; - $text =~ s/<\/dd>/<\/td><\/tr>/g; - - $output .= $text; - }; - }; - - return $output; - }; - - -# -# Function: BuildTextLink -# -# Creates a HTML link to a symbol, if it exists. -# -# Parameters: -# -# text - The link text -# package - The package <SymbolString> the link appears in, or undef if none. -# using - An arrayref of additional scope <SymbolStrings> the link has access to, or undef if none. -# sourceFile - The <FileName> the link appears in. -# -# Returns: -# -# The link in HTML, including tags. If the link doesn't resolve to anything, returns the HTML that should be substituted for it. -# -sub BuildTextLink #(text, package, using, sourceFile) - { - my ($self, $text, $package, $using, $sourceFile) = @_; - - my $plainText = $self->RestoreAmpChars($text); - - my $symbol = NaturalDocs::SymbolString->FromText($plainText); - my $target = NaturalDocs::SymbolTable->References(::REFERENCE_TEXT(), $symbol, $package, $using, $sourceFile); - - if (defined $target) - { - my $targetFile; - - if ($target->File() ne $sourceFile) - { $targetFile = $self->MakeRelativeURL( $self->OutputFileOf($sourceFile), $self->OutputFileOf($target->File()), 1 ); }; - # else leave it undef - - my $targetTooltipID = $self->BuildToolTip($target->Symbol(), $sourceFile, $target->Type(), - $target->Prototype(), $target->Summary()); - - my $toolTipProperties = $self->BuildToolTipLinkProperties($targetTooltipID); - - return '<a href="' . $targetFile . '#' . $self->SymbolToHTMLSymbol($target->Symbol()) . '" ' - . 'class=L' . NaturalDocs::Topics->NameOfType($target->Type(), 0, 1) . ' ' . $toolTipProperties . '>' . $text . '</a>'; - } - else - { - return '<' . $text . '>'; - }; - }; - - -# -# Function: BuildURLLink -# -# Creates a HTML link to an external URL. Long URLs will have hidden breaks to allow them to wrap. -# -# Parameters: -# -# url - The URL to link to. -# -# Returns: -# -# The HTML link, complete with tags. -# -sub BuildURLLink #(url) - { - my ($self, $url) = @_; - - $url = $self->RestoreAmpChars($url); - - if (length $url < 50) - { return '<a href="' . $url . '" class=LURL>' . $self->ConvertAmpChars($url) . '</a>'; }; - - my @segments = split(/([\,\&\/])/, $url); - my $output = '<a href="' . $url . '" class=LURL>'; - - # Get past the first batch of slashes, since we don't want to break on things like http://. - - $output .= $self->ConvertAmpChars($segments[0]); - - my $i = 1; - while ($i < scalar @segments && ($segments[$i] eq '/' || !$segments[$i])) - { - $output .= $segments[$i]; - $i++; - }; - - # Now break on each one of those symbols. - - while ($i < scalar @segments) - { - # Spaces don't wrap in IE for some reason. Need to use dashes as well. - if ($segments[$i] eq ',' || $segments[$i] eq '/' || $segments[$i] eq '&') - { $output .= '<span class=HB>- </span>'; }; - - $output .= $self->ConvertAmpChars($segments[$i]); - $i++; - }; - - $output .= '</a>'; - return $output; - }; - - -# -# Function: BuildEMailLink -# -# Creates a HTML link to an e-mail address. The address will be transparently munged to protect it (hopefully) from spambots. -# -# Parameters: -# -# address - The e-mail address. -# -# Returns: -# -# The HTML e-mail link, complete with tags. -# -sub BuildEMailLink #(address) - { - my ($self, $address) = @_; - my @splitAddress; - - - # Hack the address up. We want two user pieces and two host pieces. - - my ($user, $host) = split(/\@/, $address); - - my $userSplit = length($user) / 2; - - push @splitAddress, substr($user, 0, $userSplit); - push @splitAddress, substr($user, $userSplit); - - push @splitAddress, '@'; - - my $hostSplit = length($host) / 2; - - push @splitAddress, substr($host, 0, $hostSplit); - push @splitAddress, substr($host, $hostSplit); - - - # Now put it back together again. We'll use spans to split the text transparently and JavaScript to split and join the link. - - return - "<a href=\"#\" onClick=\"location.href='mai' + 'lto:' + '" . join("' + '", @splitAddress) . "'; return false;\" class=LEMail>" - . $splitAddress[0] . '<span style="display: none">.nosp@m.</span>' . $splitAddress[1] - . '<span>@</span>' - . $splitAddress[3] . '<span style="display: none">.nosp@m.</span>' . $splitAddress[4] - . '</a>'; - }; - - -# -# Function: BuildToolTipLinkProperties -# -# Returns the properties that should go in the link tag to add a tooltip to it. Because the function accepts undef, you can -# call it without checking if <BuildToolTip()> returned undef or not. -# -# Parameters: -# -# toolTipID - The ID of the tooltip. If undef, the function will return undef. -# -# Returns: -# -# The properties that should be put in the link tag, or undef if toolTipID wasn't specified. -# -sub BuildToolTipLinkProperties #(toolTipID) - { - my ($self, $toolTipID) = @_; - - if (defined $toolTipID) - { - my $currentNumber = $tooltipLinkNumber; - $tooltipLinkNumber++; - - return 'id=link' . $currentNumber . ' ' - . 'onMouseOver="ShowTip(event, \'' . $toolTipID . '\', \'link' . $currentNumber . '\')" ' - . 'onMouseOut="HideTip(\'' . $toolTipID . '\')"'; - } - else - { return undef; }; - }; - - -# -# Function: AddDoubleSpaces -# -# Adds second spaces after the appropriate punctuation with so they show up in HTML. They don't occur if there isn't at -# least one space after the punctuation, so things like class.member notation won't be affected. -# -# Parameters: -# -# text - The text to convert. -# -# Returns: -# -# The text with double spaces as necessary. -# -sub AddDoubleSpaces #(text) - { - my ($self, $text) = @_; - - # Question marks and exclamation points get double spaces unless followed by a lowercase letter. - - $text =~ s/ ([^\ \t\r\n] [\!\?]) # Must appear after a non-whitespace character to apply. - - ("|&[lr][sd]quo;|[\'\"\]\}\)]?) # Tolerate closing quotes, parenthesis, etc. - ((?:<[^>]+>)*) # Tolerate tags - - \ # The space - (?![a-z]) # Not followed by a lowercase character. - - /$1$2$3 \ /gx; - - - # Periods get double spaces if it's not followed by a lowercase letter. However, if it's followed by a capital letter and the - # preceding word is in the list of acceptable abbreviations, it won't get the double space. Yes, I do realize I am seriously - # over-engineering this. - - $text =~ s/ ([^\ \t\r\n]+) # The word prior to the period. - - \. - - ("|&[lr][sd]quo;|[\'\"\]\}\)]?) # Tolerate closing quotes, parenthesis, etc. - ((?:<[^>]+>)*) # Tolerate tags - - \ # The space - ([^a-z]) # The next character, if it's not a lowercase letter. - - /$1 . '.' . $2 . $3 . MaybeExpand($1, $4) . $4/gex; - - sub MaybeExpand #(leadWord, nextLetter) - { - my ($leadWord, $nextLetter) = @_; - - if ($nextLetter =~ /^[A-Z]$/ && exists $abbreviations{ lc($leadWord) } ) - { return ' '; } - else - { return ' '; }; - }; - - return $text; - }; - - -# -# Function: ConvertAmpChars -# -# Converts certain characters to their HTML amp char equivalents. -# -# Parameters: -# -# text - The text to convert. -# -# Returns: -# -# The converted text. -# -sub ConvertAmpChars #(text) - { - my ($self, $text) = @_; - - $text =~ s/&/&/g; - $text =~ s/\"/"/g; - $text =~ s/</</g; - $text =~ s/>/>/g; - - return $text; - }; - - -# -# Function: RestoreAmpChars -# -# Restores all amp characters to their original state. This works with both <NDMarkup> amp chars and fancy quotes. -# -# Parameters: -# -# text - The text to convert. -# -# Returns: -# -# The converted text. -# -sub RestoreAmpChars #(text) - { - my ($self, $text) = @_; - - $text = NaturalDocs::NDMarkup->RestoreAmpChars($text); - $text =~ s/&[lr]squo;/'/g; - $text =~ s/&[lr]dquo;/"/g; - - return $text; - }; - - -# -# Function: AddHiddenBreaks -# -# Adds hidden breaks to symbols. Puts them after symbol and directory separators so long names won't screw up the layout. -# -# Parameters: -# -# string - The string to break. -# -# Returns: -# -# The string with hidden breaks. -# -sub AddHiddenBreaks #(string) - { - my ($self, $string) = @_; - - # \.(?=.{5,}) instead of \. so file extensions don't get breaks. - # :+ instead of :: because Mac paths are separated by a : and we want to get those too. - - $string =~ s/(\w(?:\.(?=.{5,})|:+|->|\\|\/))(\w)/$1 . '<span class=HB> <\/span>' . $2/ge; - - return $string; - }; - -# -# Function: FindFirstFile -# -# A function that finds and returns the first file entry in the menu, or undef if none. -# -sub FindFirstFile - { - # Hidden parameter: arrayref - # Used for recursion only. - - my ($self, $arrayref) = @_; - - if (!defined $arrayref) - { $arrayref = NaturalDocs::Menu->Content(); }; - - foreach my $entry (@$arrayref) - { - if ($entry->Type() == ::MENU_FILE()) - { - return $entry; - } - elsif ($entry->Type() == ::MENU_GROUP()) - { - my $result = $self->FindFirstFile($entry->GroupContent()); - if (defined $result) - { return $result; }; - }; - }; - - return undef; - }; - - -# -# Function: ExpandMenu -# -# Determines which groups should be expanded. -# -# Parameters: -# -# sourceFile - The source <FileName> to use if you're looking for a source file. -# indexType - The index <TopicType> to use if you're looking for an index. -# selectionHierarchy - The <FileName> the menu is being built for. Does not have to be on the menu itself. -# rootLength - The length of the menu's root group, *not* including the contents of subgroups. -# -# Returns: -# -# An arrayref of all the group numbers that should be expanded. At minimum, it will contain the numbers of the groups -# present in <menuSelectionHierarchy>, though it may contain more. -# -sub ExpandMenu #(FileName sourceFile, TopicType indexType, NaturalDocs::Menu::Entry[] selectionHierarchy, int rootLength) -> int[] groupsToExpand - { - my ($self, $sourceFile, $indexType, $menuSelectionHierarchy, $rootLength) = @_; - - my $toExpand = [ ]; - - - # First expand everything in the selection hierarchy. - - my $length = $rootLength; - - foreach my $entry (@$menuSelectionHierarchy) - { - $length += $menuGroupLengths{$entry}; - push @$toExpand, $menuGroupNumbers{$entry}; - }; - - - # Now do multiple passes of group expansion as necessary. We start from bottomIndex and expand outwards. We stop going - # in a direction if a group there is too long -- we do not skip over it and check later groups as well. However, if one direction - # stops, the other can keep going. - - my $pass = 1; - my $hasSubGroups; - - while ($length < MENU_LENGTH_LIMIT) - { - my $content; - my $topIndex; - my $bottomIndex; - - - if ($pass == 1) - { - # First pass, we expand the selection's siblings. - - if (scalar @$menuSelectionHierarchy) - { $content = $menuSelectionHierarchy->[0]->GroupContent(); } - else - { $content = NaturalDocs::Menu->Content(); }; - - $bottomIndex = 0; - - while ($bottomIndex < scalar @$content && - !($content->[$bottomIndex]->Type() == ::MENU_FILE() && - $content->[$bottomIndex]->Target() eq $sourceFile) && - !($content->[$bottomIndex]->Type() != ::MENU_INDEX() && - $content->[$bottomIndex]->Target() eq $indexType) ) - { $bottomIndex++; }; - - if ($bottomIndex == scalar @$content) - { $bottomIndex = 0; }; - $topIndex = $bottomIndex - 1; - } - - elsif ($pass == 2) - { - # If the section we just expanded had no sub-groups, do another pass trying to expand the parent's sub-groups. The - # net effect is that groups won't collapse as much unnecessarily. Someone can click on a file in a sub-group and the - # groups in the parent will stay open. - - if (!$hasSubGroups && scalar @$menuSelectionHierarchy) - { - if (scalar @$menuSelectionHierarchy > 1) - { $content = $menuSelectionHierarchy->[1]->GroupContent(); } - else - { $content = NaturalDocs::Menu->Content(); }; - - $bottomIndex = 0; - - while ($bottomIndex < scalar @$content && - $content->[$bottomIndex] != $menuSelectionHierarchy->[0]) - { $bottomIndex++; }; - - $topIndex = $bottomIndex - 1; - $bottomIndex++; # Increment past our own group. - $hasSubGroups = undef; - } - else - { last; }; - } - - # No more passes. - else - { last; }; - - - while ( ($topIndex >= 0 || $bottomIndex < scalar @$content) && $length < MENU_LENGTH_LIMIT) - { - # We do the bottom first. - - while ($bottomIndex < scalar @$content && $content->[$bottomIndex]->Type() != ::MENU_GROUP()) - { $bottomIndex++; }; - - if ($bottomIndex < scalar @$content) - { - my $bottomEntry = $content->[$bottomIndex]; - $hasSubGroups = 1; - - if ($length + $menuGroupLengths{$bottomEntry} <= MENU_LENGTH_LIMIT) - { - $length += $menuGroupLengths{$bottomEntry}; - push @$toExpand, $menuGroupNumbers{$bottomEntry}; - $bottomIndex++; - } - else - { $bottomIndex = scalar @$content; }; - }; - - # Top next. - - while ($topIndex >= 0 && $content->[$topIndex]->Type() != ::MENU_GROUP()) - { $topIndex--; }; - - if ($topIndex >= 0) - { - my $topEntry = $content->[$topIndex]; - $hasSubGroups = 1; - - if ($length + $menuGroupLengths{$topEntry} <= MENU_LENGTH_LIMIT) - { - $length += $menuGroupLengths{$topEntry}; - push @$toExpand, $menuGroupNumbers{$topEntry}; - $topIndex--; - } - else - { $topIndex = -1; }; - }; - }; - - - $pass++; - }; - - return $toExpand; - }; - - -# -# Function: GetMenuSelectionHierarchy -# -# Finds the sequence of menu groups that contain the current selection. -# -# Parameters: -# -# sourceFile - The source <FileName> to use if you're looking for a source file. -# indexType - The index <TopicType> to use if you're looking for an index. -# -# Returns: -# -# An arrayref of the <NaturalDocs::Menu::Entry> objects of each group surrounding the selected menu item. First entry is the -# group immediately encompassing it, and each subsequent entry works its way towards the outermost group. -# -sub GetMenuSelectionHierarchy #(FileName sourceFile, TopicType indexType) -> NaturalDocs::Menu::Entry[] selectionHierarchy - { - my ($self, $sourceFile, $indexType) = @_; - - my $hierarchy = [ ]; - - $self->FindMenuSelection($sourceFile, $indexType, $hierarchy, NaturalDocs::Menu->Content()); - - return $hierarchy; - }; - - -# -# Function: FindMenuSelection -# -# A recursive function that deterimes if it or any of its sub-groups has the menu selection. -# -# Parameters: -# -# sourceFile - The source <FileName> to use if you're looking for a source file. -# indexType - The index <TopicType> to use if you're looking for an index. -# hierarchyRef - A reference to the menu selection hierarchy. -# entries - An arrayref of <NaturalDocs::Menu::Entries> to search. -# -# Returns: -# -# Whether this group or any of its subgroups had the selection. If true, it will add any subgroups to the menu selection -# hierarchy but not itself. This prevents the topmost entry from being added. -# -sub FindMenuSelection #(FileName sourceFile, TopicType indexType, NaturalDocs::Menu::Entry[] hierarchyRef, NaturalDocs::Menu::Entry[] entries) -> bool hasSelection - { - my ($self, $sourceFile, $indexType, $hierarchyRef, $entries) = @_; - - foreach my $entry (@$entries) - { - if ($entry->Type() == ::MENU_GROUP()) - { - # If the subgroup has the selection... - if ( $self->FindMenuSelection($sourceFile, $indexType, $hierarchyRef, $entry->GroupContent()) ) - { - push @$hierarchyRef, $entry; - return 1; - }; - } - - elsif ($entry->Type() == ::MENU_FILE()) - { - if ($sourceFile eq $entry->Target()) - { return 1; }; - } - - elsif ($entry->Type() == ::MENU_INDEX()) - { - if ($indexType eq $entry->Target) - { return 1; }; - }; - }; - - return 0; - }; - - -# -# Function: ResetToolTips -# -# Resets the <ToolTip Package Variables> for a new page. -# -# Parameters: -# -# samePage - Set this flag if there's the possibility that the next batch of tooltips may be on the same page as the last. -# -sub ResetToolTips #(samePage) - { - my ($self, $samePage) = @_; - - if (!$samePage) - { - $tooltipLinkNumber = 1; - $tooltipNumber = 1; - }; - - $tooltipHTML = undef; - %tooltipSymbolsToNumbers = ( ); - }; - - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/ClassHierarchy.pm b/docs/doctool/Modules/NaturalDocs/ClassHierarchy.pm deleted file mode 100644 index 11eb1e0c..00000000 --- a/docs/doctool/Modules/NaturalDocs/ClassHierarchy.pm +++ /dev/null @@ -1,861 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::ClassHierarchy -# -############################################################################### -# -# A package that handles all the gory details of managing the class hierarchy. It handles the hierarchy itself, which files define -# them, rebuilding the files that are affected by changes, and loading and saving them to a file. -# -# Usage and Dependencies: -# -# - <NaturalDocs::Settings> and <NaturalDocs::Project> must be initialized before use. -# -# - <NaturalDocs::SymbolTable> must be initialized before <Load()> is called. It must reflect the state as of the last time -# Natural Docs was run. -# -# - <Load()> must be called to initialize the package. At this point, the <Information Functions> will return the state as -# of the last time Natural Docs was run. You are free to resolve <NaturalDocs::SymbolTable()> afterwards. -# -# - <Purge()> must be called, and then <NaturalDocs::Parser->ParseForInformation()> must be called on all files that -# have changed so it can fully resolve the hierarchy via the <Modification Functions()>. Afterwards the -# <Information Functions> will reflect the current state of the code. -# -# - <Save()> must be called to commit any changes to the symbol table back to disk. -# -############################################################################### - -# 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; - -use NaturalDocs::ClassHierarchy::Class; -use NaturalDocs::ClassHierarchy::File; - -package NaturalDocs::ClassHierarchy; - - -############################################################################### -# Group: Variables - -# -# handle: CLASS_HIERARCHY_FILEHANDLE -# The file handle used with <ClassHierarchy.nd>. -# - -# -# hash: classes -# -# A hash of all the classes. The keys are the class <SymbolStrings> and the values are <NaturalDocs::ClassHierarchy::Classes>. -# -my %classes; - -# -# hash: files -# -# A hash of the hierarchy information referenced by file. The keys are the <FileNames>, and the values are -# <NaturalDocs::ClassHierarchy::File>s. -# -my %files; - -# -# hash: parentReferences -# -# A hash of all the parent reference strings and what they resolve to. The keys are the <ReferenceStrings> and the values are -# the class <SymbolStrings> that they resolve to. -# -my %parentReferences; - -# -# object: watchedFile -# -# A <NaturalDocs::ClassHierarchy::File> object of the file being watched for changes. This is compared to the version in <files> -# to see if anything was changed since the last parse. -# -my $watchedFile; - -# -# string: watchedFileName -# -# The <FileName> of the watched file, if any. If there is no watched file, this will be undef. -# -my $watchedFileName; - -# -# bool: dontRebuildFiles -# -# A bool to set if you don't want changes in the hierarchy to cause files to be rebuilt. -# -my $dontRebuildFiles; - - - -############################################################################### -# Group: Files - - -# -# File: ClassHierarchy.nd -# -# Stores the class hierarchy on disk. -# -# Format: -# -# > [BINARY_FORMAT] -# > [VersionInt: app version] -# -# The standard <BINARY_FORMAT> and <VersionInt> header. -# -# > [SymbolString: class or undef to end] -# -# Next we begin a class segment with its <SymbolString>. These continue until the end of the file. Only defined classes are -# included. -# -# > [UInt32: number of files] -# > [AString16: file] [AString16: file] ... -# -# Next there is the number of files that define that class. It's a UInt32, which seems like overkill, but I could imagine every -# file in a huge C++ project being under the same namespace, and thus contributing its own definition. It's theoretically -# possible. -# -# Following the number is that many file names. You must remember the index of each file, as they will be important later. -# Indexes start at one because zero has a special meaning. -# -# > [UInt8: number of parents] -# > ( [ReferenceString (no type): parent] -# > [UInt32: file index] [UInt32: file index] ... [UInt32: 0] ) ... -# -# Next there is the number of parents defined for this class. For each one, we define a parent segment, which consists of -# its <ReferenceString>, and then a zero-terminated string of indexes of the files that define that parent as part of that class. -# The indexes start at one, and are into the list of files we saw previously. -# -# Note that we do store class segments for classes without parents, but not for undefined classes. -# -# This concludes a class segment. These segments continue until an undef <SymbolString>. -# -# See Also: -# -# <File Format Conventions> -# -# Revisions: -# -# 1.22: -# -# - Classes and parents switched from AString16s to <SymbolStrings> and <ReferenceStrings>. -# - A ending undef <SymbolString> was added to the end. Previously it stopped when the file ran out. -# -# 1.2: -# -# - This file was introduced in 1.2. -# - - -############################################################################### -# Group: File Functions - - -# -# Function: Load -# -# Loads the class hierarchy from disk. -# -sub Load - { - my ($self) = @_; - - $dontRebuildFiles = 1; - - my $fileIsOkay = 1; - my $fileName = NaturalDocs::Project->ClassHierarchyFile(); - - if (!open(CLASS_HIERARCHY_FILEHANDLE, '<' . $fileName)) - { $fileIsOkay = undef; } - else - { - # See if it's binary. - binmode(CLASS_HIERARCHY_FILEHANDLE); - - my $firstChar; - read(CLASS_HIERARCHY_FILEHANDLE, $firstChar, 1); - - if ($firstChar != ::BINARY_FORMAT()) - { - close(CLASS_HIERARCHY_FILEHANDLE); - $fileIsOkay = undef; - } - else - { - my $version = NaturalDocs::Version->FromBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE); - - # Minor bugs were fixed in 1.33 that may affect the stored data. - - if ($version > NaturalDocs::Settings->AppVersion() || $version < NaturalDocs::Version->FromString('1.33')) - { - close(CLASS_HIERARCHY_FILEHANDLE); - $fileIsOkay = undef; - }; - }; - }; - - - if (!$fileIsOkay) - { - NaturalDocs::Project->ReparseEverything(); - } - else - { - my $raw; - - for (;;) - { - # [SymbolString: class or undef to end] - - my $class = NaturalDocs::SymbolString->FromBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE); - - if (!defined $class) - { last; }; - - # [UInt32: number of files] - - read(CLASS_HIERARCHY_FILEHANDLE, $raw, 4); - my $numberOfFiles = unpack('N', $raw); - - my @files; - - while ($numberOfFiles) - { - # [AString16: file] - - read(CLASS_HIERARCHY_FILEHANDLE, $raw, 2); - my $fileLength = unpack('n', $raw); - - my $file; - read(CLASS_HIERARCHY_FILEHANDLE, $file, $fileLength); - - push @files, $file; - $self->AddClass($file, $class); - - $numberOfFiles--; - }; - - # [UInt8: number of parents] - - read(CLASS_HIERARCHY_FILEHANDLE, $raw, 1); - my $numberOfParents = unpack('C', $raw); - - while ($numberOfParents) - { - # [ReferenceString (no type): parent] - - my $parent = NaturalDocs::ReferenceString->FromBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, - ::BINARYREF_NOTYPE(), - ::REFERENCE_CH_PARENT()); - - for (;;) - { - # [UInt32: file index or 0] - - read(CLASS_HIERARCHY_FILEHANDLE, $raw, 4); - my $fileIndex = unpack('N', $raw); - - if ($fileIndex == 0) - { last; } - - $self->AddParentReference( $files[$fileIndex - 1], $class, $parent ); - }; - - $numberOfParents--; - }; - }; - - close(CLASS_HIERARCHY_FILEHANDLE); - }; - - $dontRebuildFiles = undef; - }; - - -# -# Function: Save -# -# Saves the class hierarchy to disk. -# -sub Save - { - my ($self) = @_; - - open (CLASS_HIERARCHY_FILEHANDLE, '>' . NaturalDocs::Project->ClassHierarchyFile()) - or die "Couldn't save " . NaturalDocs::Project->ClassHierarchyFile() . ".\n"; - - binmode(CLASS_HIERARCHY_FILEHANDLE); - - print CLASS_HIERARCHY_FILEHANDLE '' . ::BINARY_FORMAT(); - NaturalDocs::Version->ToBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, NaturalDocs::Settings->AppVersion()); - - while (my ($class, $classObject) = each %classes) - { - if ($classObject->IsDefined()) - { - # [SymbolString: class or undef to end] - - NaturalDocs::SymbolString->ToBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, $class); - - # [UInt32: number of files] - - my @definitions = $classObject->Definitions(); - my %definitionIndexes; - - print CLASS_HIERARCHY_FILEHANDLE pack('N', scalar @definitions); - - for (my $i = 0; $i < scalar @definitions; $i++) - { - # [AString16: file] - print CLASS_HIERARCHY_FILEHANDLE pack('nA*', length($definitions[$i]), $definitions[$i]); - $definitionIndexes{$definitions[$i]} = $i + 1; - }; - - # [UInt8: number of parents] - - my @parents = $classObject->ParentReferences(); - print CLASS_HIERARCHY_FILEHANDLE pack('C', scalar @parents); - - foreach my $parent (@parents) - { - # [ReferenceString (no type): parent] - - NaturalDocs::ReferenceString->ToBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, $parent, ::BINARYREF_NOTYPE()); - - # [UInt32: file index] - - my @parentDefinitions = $classObject->ParentReferenceDefinitions($parent); - - foreach my $parentDefinition (@parentDefinitions) - { - print CLASS_HIERARCHY_FILEHANDLE pack('N', $definitionIndexes{$parentDefinition}); - }; - - # [UInt32: 0] - print CLASS_HIERARCHY_FILEHANDLE pack('N', 0); - }; - }; - }; - - # [SymbolString: class or undef to end] - - NaturalDocs::SymbolString->ToBinaryFile(\*CLASS_HIERARCHY_FILEHANDLE, undef); - - close(CLASS_HIERARCHY_FILEHANDLE); - }; - - -# -# Function: Purge -# -# Purges the hierarchy of files that no longer have Natural Docs content. -# -sub Purge - { - my ($self) = @_; - - my $filesToPurge = NaturalDocs::Project->FilesToPurge(); - - foreach my $file (keys %$filesToPurge) - { - $self->DeleteFile($file); - }; - }; - - - -############################################################################### -# Group: Interface Functions - - -# -# Function: OnInterpretationChange -# -# Called by <NaturalDocs::SymbolTable> whenever a class hierarchy reference's intepretation changes, meaning it switched -# from one symbol to another. -# -# reference - The <ReferenceString> whose current interpretation changed. -# -sub OnInterpretationChange #(reference) - { - my ($self, $reference) = @_; - - if (NaturalDocs::ReferenceString->TypeOf($reference) == ::REFERENCE_CH_PARENT()) - { - # The approach here is simply to completely delete the reference and readd it. This is less than optimal efficiency, since it's - # being removed and added from %files too, even though that isn't required. However, the simpler code is worth it - # considering this will only happen when a parent reference becomes defined or undefined, or on the rare languages (like C#) - # that allow relative parent references. - - my $oldTargetSymbol = $parentReferences{$reference}; - my $oldTargetObject = $classes{$oldTargetSymbol}; - - my @classesWithReferenceParent = $oldTargetObject->Children(); - - # Each entry is an arrayref of file names. Indexes are the same as classesWithReferenceParent's. - my @filesDefiningReferenceParent; - - foreach my $classWithReferenceParent (@classesWithReferenceParent) - { - my $fileList = [ $classes{$classWithReferenceParent}->ParentReferenceDefinitions($reference) ]; - push @filesDefiningReferenceParent, $fileList; - - foreach my $fileDefiningReferenceParent (@$fileList) - { - $self->DeleteParentReference($fileDefiningReferenceParent, $classWithReferenceParent, $reference); - }; - }; - - - # This will force the reference to be reinterpreted on the next add. - - delete $parentReferences{$reference}; - - - # Now we can just readd it. - - for (my $i = 0; $i < scalar @classesWithReferenceParent; $i++) - { - foreach my $file (@{$filesDefiningReferenceParent[$i]}) - { - $self->AddParentReference($file, $classesWithReferenceParent[$i], $reference); - }; - }; - }; - - # The only way for a REFERENCE_CH_CLASS reference to change is if the symbol is deleted. That will be handled by - # <AnalyzeChanges()>, so we don't need to do anything here. - }; - - -# -# Function: OnTargetSymbolChange -# -# Called by <NaturalDocs::SymbolTable> whenever a class hierarchy reference's target symbol changes, but the reference -# still resolves to the same symbol. -# -# Parameters: -# -# reference - The <ReferenceString> that was affected by the change. -# -sub OnTargetSymbolChange #(reference) - { - my ($self, $reference) = @_; - - my $type = NaturalDocs::ReferenceString->TypeOf($reference); - my $class; - - if ($type == ::REFERENCE_CH_PARENT()) - { $class = $parentReferences{$reference}; } - else # ($type == ::REFERENCE_CH_CLASS()) - { - # Class references are global absolute, so we can just yank the symbol. - (undef, $class, undef, undef, undef) = NaturalDocs::ReferenceString->InformationOf($reference); - }; - - $self->RebuildFilesFor($class, 1, 0, 1); - }; - - - -############################################################################### -# Group: Modification Functions - - -# -# Function: AddClass -# -# Adds a class to the hierarchy. -# -# Parameters: -# -# file - The <FileName> the class was defined in. -# class - The class <SymbolString>. -# -# Note: -# -# The file parameter must be defined when using this function externally. It may be undef for internal use only. -# -sub AddClass #(file, class) - { - my ($self, $file, $class) = @_; - - if (!exists $classes{$class}) - { - $classes{$class} = NaturalDocs::ClassHierarchy::Class->New(); - NaturalDocs::SymbolTable->AddReference($self->ClassReferenceOf($class), $file) - }; - - if (defined $file) - { - # If this was the first definition for this class... - if ($classes{$class}->AddDefinition($file)) - { $self->RebuildFilesFor($class, 1, 1, 1); }; - - if (!exists $files{$file}) - { $files{$file} = NaturalDocs::ClassHierarchy::File->New(); }; - - $files{$file}->AddClass($class); - - if (defined $watchedFileName) - { $watchedFile->AddClass($class); }; - }; - }; - - -# -# Function: AddParentReference -# -# Adds a class-parent relationship to the hierarchy. The classes will be created if they don't already exist. -# -# Parameters: -# -# file - The <FileName> the reference was defined in. -# class - The class <SymbolString>. -# symbol - The parent class <SymbolString>. -# scope - The package <SymbolString> that the reference appeared in. -# using - An arrayref of package <SymbolStrings> that the reference has access to via "using" statements. -# resolvingFlags - Any <Resolving Flags> to be used when resolving the reference. -# -# Alternate Parameters: -# -# file - The <FileName> the reference was defined in. -# class - The class <SymbolString>. -# reference - The parent <ReferenceString>. -# -sub AddParentReference #(file, class, symbol, scope, using, resolvingFlags) or (file, class, reference) - { - my ($self, $file, $class, $symbol, $parentReference); - - if (scalar @_ == 7) - { - my ($scope, $using, $resolvingFlags); - ($self, $file, $class, $symbol, $scope, $using, $resolvingFlags) = @_; - - $parentReference = NaturalDocs::ReferenceString->MakeFrom(::REFERENCE_CH_PARENT(), - $symbol, $scope, $using, $resolvingFlags); - } - else - { - ($self, $file, $class, $parentReference) = @_; - $symbol = (NaturalDocs::ReferenceString->InformationOf($parentReference))[1]; - }; - - - # In case it doesn't already exist. - $self->AddClass($file, $class); - - my $parent; - if (exists $parentReferences{$parentReference}) - { - $parent = $parentReferences{$parentReference}; - } - else - { - NaturalDocs::SymbolTable->AddReference($parentReference, $file); - my $parentTarget = NaturalDocs::SymbolTable->References($parentReference); - - if (defined $parentTarget) - { $parent = $parentTarget->Symbol(); } - else - { $parent = $symbol; }; - - # In case it doesn't already exist. - $self->AddClass(undef, $parent); - - $parentReferences{$parentReference} = $parent; - }; - - - # If this defined a new parent... - if ($classes{$class}->AddParentReference($parentReference, $file, \%parentReferences)) - { - $classes{$parent}->AddChild($class); - - $self->RebuildFilesFor($class, 0, 1, 0); - $self->RebuildFilesFor($parent, 0, 1, 0); - }; - - $files{$file}->AddParentReference($class, $parentReference); - - if (defined $watchedFileName) - { $watchedFile->AddParentReference($class, $parentReference); }; - }; - - -# -# Function: WatchFileForChanges -# -# Watches a file for changes, which can then be applied by <AnalyzeChanges()>. Definitions are not deleted via a DeleteClass() -# function. Instead, a file is watched for changes, reparsed, and then a comparison is made to look for definitions that -# disappeared and any other relevant changes. -# -# Parameters: -# -# file - The <FileName> to watch. -# -sub WatchFileForChanges #(file) - { - my ($self, $file) = @_; - - $watchedFile = NaturalDocs::ClassHierarchy::File->New(); - $watchedFileName = $file; - }; - - -# -# Function: AnalyzeChanges -# -# Checks the watched file for any changes that occured since the last time is was parsed, and updates the hierarchy as -# necessary. Also sends any files that are affected to <NaturalDocs::Project->RebuildFile()>. -# -sub AnalyzeChanges - { - my ($self) = @_; - - # If the file didn't have any classes before, and it still doesn't, it wont be in %files. - if (exists $files{$watchedFileName}) - { - my @originalClasses = $files{$watchedFileName}->Classes(); - - foreach my $originalClass (@originalClasses) - { - # If the class isn't there the second time around... - if (!$watchedFile->HasClass($originalClass)) - { $self->DeleteClass($watchedFileName, $originalClass); } - - else - { - my @originalParents = $files{$watchedFileName}->ParentReferencesOf($originalClass); - - foreach my $originalParent (@originalParents) - { - # If the parent reference wasn't there the second time around... - if (!$watchedFile->HasParentReference($originalClass, $originalParent)) - { $self->DeleteParentReference($watchedFileName, $originalClass, $originalParent); }; - }; - }; - }; - }; - - - $watchedFile = undef; - $watchedFileName = undef; - }; - - - -############################################################################### -# Group: Information Functions - - -# -# Function: ParentsOf -# Returns a <SymbolString> array of the passed class' parents, or an empty array if none. Note that not all of them may be -# defined. -# -sub ParentsOf #(class) - { - my ($self, $class) = @_; - - if (exists $classes{$class}) - { return $classes{$class}->Parents(); } - else - { return ( ); }; - }; - -# -# Function: ChildrenOf -# Returns a <SymbolString> array of the passed class' children, or an empty array if none. Note that not all of them may be -# defined. -# -sub ChildrenOf #(class) - { - my ($self, $class) = @_; - - if (exists $classes{$class}) - { return $classes{$class}->Children(); } - else - { return ( ); }; - }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: DeleteFile -# -# Deletes a file and everything defined in it. -# -# Parameters: -# -# file - The <FileName>. -# -sub DeleteFile #(file) - { - my ($self, $file) = @_; - - if (!exists $files{$file}) - { return; }; - - my @classes = $files{$file}->Classes(); - foreach my $class (@classes) - { - $self->DeleteClass($file, $class); - }; - - delete $files{$file}; - }; - -# -# Function: DeleteClass -# -# Deletes a class definition from a file. Will also delete any parent references from this class and file. Will rebuild any file -# affected unless <dontRebuildFiles> is set. -# -# Parameters: -# -# file - The <FileName> that defines the class. -# class - The class <SymbolString>. -# -sub DeleteClass #(file, class) - { - my ($self, $file, $class) = @_; - - my @parents = $files{$file}->ParentReferencesOf($class); - foreach my $parent (@parents) - { - $self->DeleteParentReference($file, $class, $parent); - }; - - $files{$file}->DeleteClass($class); - - # If we're deleting the last definition of this class. - if ($classes{$class}->DeleteDefinition($file)) - { - if (!$classes{$class}->HasChildren()) - { - delete $classes{$class}; - - if (!$dontRebuildFiles) - { NaturalDocs::Project->RebuildFile($file); }; - } - else - { $self->RebuildFilesFor($class, 0, 1, 1); }; - - }; - }; - - -# -# Function: DeleteParentReference -# -# Deletes a class' parent reference and returns whether it resulted in the loss of a parent class. Will rebuild any file affected -# unless <dontRebuildFiles> is set. -# -# Parameters: -# -# file - The <FileName> that defines the reference. -# class - The class <SymbolString>. -# reference - The parent <ReferenceString>. -# -# Returns: -# -# If the class lost a parent as a result of this, it will return its <SymbolString>. It will return undef otherwise. -# -sub DeleteParentReference #(file, class, reference) - { - my ($self, $file, $class, $reference) = @_; - - if (!exists $classes{$class}) - { return; }; - - $files{$file}->DeleteParentReference($class, $reference); - - my $deletedParent = $classes{$class}->DeleteParentReference($reference, $file, \%parentReferences); - - if (defined $deletedParent) - { - my $deletedParentObject = $classes{$deletedParent}; - - $deletedParentObject->DeleteChild($class); - - $self->RebuildFilesFor($deletedParent, 0, 1, 0); - $self->RebuildFilesFor($class, 0, 1, 0); - - if (!$deletedParentObject->HasChildren() && !$deletedParentObject->IsDefined()) - { - delete $classes{$deletedParent}; - NaturalDocs::SymbolTable->DeleteReference( $self->ClassReferenceOf($class) ); - }; - - return $deletedParent; - }; - - return undef; - }; - - -# -# Function: ClassReferenceOf -# -# Returns the <REFERENCE_CH_CLASS> <ReferenceString> of the passed class <SymbolString>. -# -sub ClassReferenceOf #(class) - { - my ($self, $class) = @_; - - return NaturalDocs::ReferenceString->MakeFrom(::REFERENCE_CH_CLASS(), $class, undef, undef, - ::RESOLVE_ABSOLUTE() | ::RESOLVE_NOPLURAL()); - }; - - -# -# Function: RebuildFilesFor -# -# Calls <NaturalDocs::Project->RebuildFile()> for every file defining the passed class, its parents, and/or its children. -# Returns without doing anything if <dontRebuildFiles> is set. -# -# Parameters: -# -# class - The class <SymbolString>. -# rebuildParents - Whether to rebuild the class' parents. -# rebuildSelf - Whether to rebuild the class. -# rebuildChildren - Whether to rebuild the class' children. -# -sub RebuildFilesFor #(class, rebuildParents, rebuildSelf, rebuildChildren) - { - my ($self, $class, $rebuildParents, $rebuildSelf, $rebuildChildren) = @_; - - if ($dontRebuildFiles) - { return; }; - - my @classesToBuild; - - if ($rebuildParents) - { @classesToBuild = $classes{$class}->Parents(); }; - if ($rebuildSelf) - { push @classesToBuild, $class; }; - if ($rebuildChildren) - { push @classesToBuild, $classes{$class}->Children(); }; - - foreach my $classToBuild (@classesToBuild) - { - my @definitions = $classes{$classToBuild}->Definitions(); - - foreach my $definition (@definitions) - { NaturalDocs::Project->RebuildFile($definition); }; - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/ClassHierarchy/Class.pm b/docs/doctool/Modules/NaturalDocs/ClassHierarchy/Class.pm deleted file mode 100644 index c3ed4aef..00000000 --- a/docs/doctool/Modules/NaturalDocs/ClassHierarchy/Class.pm +++ /dev/null @@ -1,412 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::ClassHierarchy::Class -# -############################################################################### -# -# An object that stores information about a class in the hierarchy. It does not store its <SymbolString>; it assumes that it will -# be stored in a hashref where the key is the <SymbolString>. -# -############################################################################### - -# 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::ClassHierarchy::Class; - - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The keys are the constants below. -# -# DEFINITIONS - An existence hashref of all the <FileNames> which define this class. Undef if none. -# PARENTS - An existence hashref of the <SymbolStrings> of all the parents this class has. -# CHILDREN - An existence hashref of the <SymbolStrings> of all the children this class has. -# PARENT_REFERENCES - A hashref of the parent <ReferenceStrings> this class has. The keys are the <ReferenceStrings>, -# and the values are existence hashrefs of all the <FileNames> that define them. Undef if none. -# -use NaturalDocs::DefineMembers 'DEFINITIONS', 'PARENTS', 'CHILDREN', 'PARENT_REFERENCES'; -# Dependency: New() depends on the order of these constants, as well as the class not being derived from any other. - - -############################################################################### -# Group: Modification Functions - - -# -# Function: New -# -# Creates and returns a new class. -# -sub New - { - # Dependency: This function depends on the order of the constants, as well as the class not being derived from any other. - my ($package, $definitionFile) = @_; - - my $object = [ undef, undef, undef, undef ]; - bless $object, $package; - - return $object; - }; - - -# -# Function: AddDefinition -# -# Adds a rew definition of this class and returns if that was the first definition. -# -# Parameters: -# -# file - The <FileName> the definition appears in. -# -# Returns: -# -# Whether this was the first definition of this class. -# -sub AddDefinition #(file) - { - my ($self, $file) = @_; - - my $wasFirst; - - if (!defined $self->[DEFINITIONS]) - { - $self->[DEFINITIONS] = { }; - $wasFirst = 1; - }; - - $self->[DEFINITIONS]->{$file} = 1; - - return $wasFirst; - }; - - -# -# Function: DeleteDefinition -# -# Removes the definition of this class and returns if there are no more definitions. Note that if there are no more -# definitions, you may still want to keep the object around if <HasChildren()> returns true. -# -# Parameters: -# -# file - The <FileName> the definition appears in. -# -# Returns: -# -# Whether this deleted the last definition of this class. -# -sub DeleteDefinition #(file) - { - my ($self, $file) = @_; - - if (defined $self->[DEFINITIONS]) - { - delete $self->[DEFINITIONS]->{$file}; - - if (!scalar keys %{$self->[DEFINITIONS]}) - { - $self->[DEFINITIONS] = undef; - return 1; - }; - }; - - return undef; - }; - - -# -# Function: AddParentReference -# -# Adds a parent reference to the class and return whether it resulted in a new parent class. -# -# Parameters: -# -# reference - The <ReferenceString> used to determine the parent. -# file - The <FileName> the parent reference is in. -# referenceTranslations - A hashref of what each reference currently resolves to. The keys are the -# <ReferenceStrings> and the values are class <SymbolStrings>. It should include an entry for -# the reference parameter above. -# -# Returns: -# -# If the reference adds a new parent, it will return that parent's <SymbolString>. Otherwise it will return undef. -# -sub AddParentReference #(reference, file, referenceTranslations) - { - my ($self, $reference, $file, $referenceTranslations) = @_; - - if (!defined $self->[PARENT_REFERENCES]) - { $self->[PARENT_REFERENCES] = { }; }; - if (!defined $self->[PARENTS]) - { $self->[PARENTS] = { }; }; - - - if (!exists $self->[PARENT_REFERENCES]->{$reference}) - { - $self->[PARENT_REFERENCES]->{$reference} = { $file => 1 }; - - my $symbol = $referenceTranslations->{$reference}; - - if (!exists $self->[PARENTS]->{$symbol}) - { - $self->[PARENTS]->{$symbol} = 1; - return $symbol; - } - else - { return undef; }; - } - else - { - $self->[PARENT_REFERENCES]->{$reference}->{$file} = 1; - return undef; - }; - }; - -# -# Function: DeleteParentReference -# -# Deletes a parent reference from the class and return whether it resulted in a loss of a parent class. -# -# Parameters: -# -# reference - The <ReferenceString> used to determine the parent. -# file - The <FileName> the parent declaration is in. -# referenceTranslations - A hashref of what each reference currently resolves to. The keys are the -# <ReferenceStrings> and the values are class <SymbolStrings>. It should include an entry for -# the reference parameter above. -# -# Returns: -# -# If this causes a parent class to be lost, it will return that parent's <SymbolString>. Otherwise it will return undef. -# -sub DeleteParentReference #(reference, file, referenceTranslations) - { - my ($self, $reference, $file, $referenceTranslations) = @_; - - if (defined $self->[PARENT_REFERENCES] && exists $self->[PARENT_REFERENCES]->{$reference} && - exists $self->[PARENT_REFERENCES]->{$reference}->{$file}) - { - delete $self->[PARENT_REFERENCES]->{$reference}->{$file}; - - # Quit if there are other definitions of this reference. - if (scalar keys %{$self->[PARENT_REFERENCES]->{$reference}}) - { return undef; }; - - delete $self->[PARENT_REFERENCES]->{$reference}; - - if (!scalar keys %{$self->[PARENT_REFERENCES]}) - { $self->[PARENT_REFERENCES] = undef; }; - - my $parent = $referenceTranslations->{$reference}; - - # Check if any other references resolve to the same parent. - if (defined $self->[PARENT_REFERENCES]) - { - foreach my $parentReference (keys %{$self->[PARENT_REFERENCES]}) - { - if ($referenceTranslations->{$parentReference} eq $parent) - { return undef; }; - }; - }; - - # If we got this far, no other parent references resolve to this symbol. - - delete $self->[PARENTS]->{$parent}; - - if (!scalar keys %{$self->[PARENTS]}) - { $self->[PARENTS] = undef; }; - - return $parent; - } - else - { return undef; }; - }; - - -# -# Function: AddChild -# Adds a child <SymbolString> to the class. Unlike <AddParentReference()>, this does not keep track of anything other than -# whether it has it or not. -# -# Parameters: -# -# child - The <SymbolString> to add. -# -sub AddChild #(child) - { - my ($self, $child) = @_; - - if (!defined $self->[CHILDREN]) - { $self->[CHILDREN] = { }; }; - - $self->[CHILDREN]->{$child} = 1; - }; - -# -# Function: DeleteChild -# Deletes a child <SymbolString> from the class. Unlike <DeleteParentReference()>, this does not keep track of anything other -# than whether it has it or not. -# -# Parameters: -# -# child - The <SymbolString> to delete. -# -sub DeleteChild #(child) - { - my ($self, $child) = @_; - - if (defined $self->[CHILDREN]) - { - delete $self->[CHILDREN]->{$child}; - - if (!scalar keys %{$self->[CHILDREN]}) - { $self->[CHILDREN] = undef; }; - }; - }; - - - -############################################################################### -# Group: Information Functions - -# -# Function: Definitions -# Returns an array of the <FileNames> that define this class, or an empty array if none. -# -sub Definitions - { - my ($self) = @_; - - if (defined $self->[DEFINITIONS]) - { return keys %{$self->[DEFINITIONS]}; } - else - { return ( ); }; - }; - -# -# Function: IsDefinedIn -# Returns whether the class is defined in the passed <FileName>. -# -sub IsDefinedIn #(file) - { - my ($self, $file) = @_; - - if (defined $self->[DEFINITIONS]) - { return exists $self->[DEFINITIONS]->{$file}; } - else - { return 0; }; - }; - -# -# Function: IsDefined -# Returns whether the class is defined in any files. -# -sub IsDefined - { - my ($self) = @_; - return defined $self->[DEFINITIONS]; - }; - -# -# Function: ParentReferences -# Returns an array of the parent <ReferenceStrings>, or an empty array if none. -# -sub ParentReferences - { - my ($self) = @_; - - if (defined $self->[PARENT_REFERENCES]) - { return keys %{$self->[PARENT_REFERENCES]}; } - else - { return ( ); }; - }; - -# -# Function: HasParentReference -# Returns whether the class has the passed parent <ReferenceString>. -# -sub HasParentReference #(reference) - { - my ($self, $reference) = @_; - return (defined $self->[PARENT_REFERENCES] && exists $self->[PARENT_REFERENCES]->{$reference}); - }; - -# -# Function: HasParentReferences -# Returns whether the class has any parent <ReferenceStrings>. -# -sub HasParentReferences - { - my ($self) = @_; - return defined $self->[PARENT_REFERENCES]; - }; - -# -# Function: Parents -# Returns an array of the parent <SymbolStrings>, or an empty array if none. -# -sub Parents - { - my ($self) = @_; - - if (defined $self->[PARENTS]) - { return keys %{$self->[PARENTS]}; } - else - { return ( ); }; - }; - -# -# Function: HasParents -# Returns whether the class has any parent <SymbolStrings> defined. -# -sub HasParents - { - my ($self) = @_; - return defined $self->[PARENTS]; - }; - -# -# Function: Children -# Returns an array of the child <SymbolStrings>, or an empty array if none. -# -sub Children - { - my ($self) = @_; - - if (defined $self->[CHILDREN]) - { return keys %{$self->[CHILDREN]}; } - else - { return ( ); }; - }; - -# -# Function: HasChildren -# Returns whether any child <SymbolStrings> are defined. -# -sub HasChildren - { - my ($self) = @_; - return defined $self->[CHILDREN]; - }; - - -# -# Function: ParentReferenceDefinitions -# Returns an array of the <FileNames> which define the passed parent <ReferenceString>, or an empty array if none. -# -sub ParentReferenceDefinitions #(reference) - { - my ($self, $reference) = @_; - - if (defined $self->[PARENT_REFERENCES] && exists $self->[PARENT_REFERENCES]->{$reference}) - { return keys %{$self->[PARENT_REFERENCES]->{$reference}}; } - else - { return ( ); }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/ClassHierarchy/File.pm b/docs/doctool/Modules/NaturalDocs/ClassHierarchy/File.pm deleted file mode 100644 index 19d17229..00000000 --- a/docs/doctool/Modules/NaturalDocs/ClassHierarchy/File.pm +++ /dev/null @@ -1,157 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::ClassHierarchy::File -# -############################################################################### -# -# An object that stores information about what hierarchy information is present in a file. It does not store its <FileName>; it -# assumes that it will be stored in a hashref where the key is the <FileName>. -# -############################################################################### - -# 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::ClassHierarchy::File; - - -# -# Topic: Implementation -# -# Since there's only one member in the class, and it's a hashref, the class is simply the hashref itself blessed as a class. -# The keys are the class <SymbolStrings> that are defined in the file, and the values are existence hashrefs of each class' -# parent <ReferenceStrings>, or undef if none. -# - - -############################################################################### -# Group: Modification Functions - - -# -# Function: New -# -# Creates and returns a new class. -# -sub New - { - my ($package) = @_; - - my $object = { }; - bless $object, $package; - - return $object; - }; - -# -# Function: AddClass -# Adds a rew class <SymbolString> to the file. -# -sub AddClass #(class) - { - my ($self, $class) = @_; - - if (!exists $self->{$class}) - { $self->{$class} = undef; }; - }; - -# -# Function: DeleteClass -# Deletes a class <SymbolString> from the file. -# -sub DeleteClass #(class) - { - my ($self, $class) = @_; - delete $self->{$class}; - }; - -# -# Function: AddParentReference -# Adds a parent <ReferenceString> to a class <SymbolString>. -# -sub AddParentReference #(class, parentReference) - { - my ($self, $class, $parent) = @_; - - if (!exists $self->{$class} || !defined $self->{$class}) - { $self->{$class} = { }; }; - - $self->{$class}->{$parent} = 1; - }; - -# -# Function: DeleteParentReference -# Deletes a parent <ReferenceString> from a class <SymbolString>. -# -sub DeleteParentReference #(class, parent) - { - my ($self, $class, $parent) = @_; - - if (exists $self->{$class}) - { - delete $self->{$class}->{$parent}; - - if (!scalar keys %{$self->{$class}}) - { $self->{$class} = undef; }; - }; - }; - - - -############################################################################### -# Group: Information Functions - - -# -# Function: Classes -# Returns an array of the class <SymbolStrings> that are defined by this file, or an empty array if none. -# -sub Classes - { - my ($self) = @_; - return keys %{$self}; - }; - -# -# Function: HasClass -# Returns whether the file defines the passed class <SymbolString>. -# -sub HasClass #(class) - { - my ($self, $class) = @_; - return exists $self->{$class}; - }; - -# -# Function: ParentReferencesOf -# Returns an array of the parent <ReferenceStrings> that are defined by the class, or an empty array if none. -# -sub ParentReferencesOf #(class) - { - my ($self, $class) = @_; - - if (!exists $self->{$class} || !defined $self->{$class}) - { return ( ); } - else - { return keys %{$self->{$class}}; }; - }; - -# -# Function: HasParentReference -# Returns whether the file defines the passed class <SymbolString> and parent <ReferenceString>. -# -sub HasParentReference #(class, parent) - { - my ($self, $class, $parent) = @_; - - if (!$self->HasClass($class)) - { return undef; }; - - return exists $self->{$class}->{$parent}; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/ConfigFile.pm b/docs/doctool/Modules/NaturalDocs/ConfigFile.pm deleted file mode 100644 index 9a20fc5e..00000000 --- a/docs/doctool/Modules/NaturalDocs/ConfigFile.pm +++ /dev/null @@ -1,497 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::ConfigFile -# -############################################################################### -# -# A package to manage Natural Docs' configuration files. -# -# Usage: -# -# - Only one configuration file can be managed with this package at a time. You must close the file before opening another -# one. -# -############################################################################### - -# 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::ConfigFile; - - - -# -# Topic: Format -# -# All configuration files are text files. -# -# > # [comment] -# -# Comments start with the # character. -# -# > Format: [version] -# -# All configuration files *must* have a format line as its first line containing content. Whitespace and comments are permitted -# ahead of it. -# -# > [keyword]: [value] -# -# Keywords can only contain <CFChars>. Keywords are not case sensitive. Values can be anything and run until the end of -# the line or a comment. -# -# > [value] -# -# Lines that don't start with a valid keyword format are considered to be all value. -# -# > [line] { [line] } [line] -# -# Files supporting brace groups (specified in <Open()>) may also have braces that can appear anywhere. It allows more than -# one thing to appear per line, which isn't supported otherwise. Consequently, values may not have braces. -# - - -# -# Type: CFChars -# -# The characters that can appear in configuration file keywords and user-defined element names: letters, numbers, spaces, -# dashes, slashes, apostrophes, and periods. -# -# Although the list above is exhaustive, it should be noted that you especially can *not* use colons (messes up keyword: value -# sequences) commas (messes up item, item, item list sequences) and hashes (messes up comment detection.) -# -# You can search the source code for [CFChars] to find all the instances where this definition is used. -# - - -############################################################################### -# Group: Variables - -# -# handle: CONFIG_FILEHANDLE -# -# The file handle used for the configuration file. -# - - -# -# string: file -# -# The <FileName> for the current configuration file being parsed. -# -my $file; - - -# -# array: errors -# -# An array of errors added by <AddError()>. Every odd entry is the line number, and every even entry following is the -# error message. -# -my @errors; - - -# -# var: lineNumber -# -# The current line number for the configuration file. -# -my $lineNumber; - - -# -# bool: hasBraceGroups -# -# Whether the file has brace groups or not. -# -my $hasBraceGroups; - - -# -# array: virtualLines -# -# An array of virtual lines if a line from the file contained more than one. -# -# Files with brace groups may have more than one virtual line per actual file line, such as "Group: A { Group: B". When that -# happens, any extra virtual lines are put into here so they can be returned on the next call. -# -my @virtualLines; - - - -############################################################################### -# Group: Functions - - -# -# Function: Open -# -# Opens a configuration file for parsing and returns the format <VersionInt>. -# -# Parameters: -# -# file - The <FileName> to parse. -# hasBraceGroups - Whether the file supports brace groups or not. If so, lines with braces will be split apart behind the -# scenes. -# -# Returns: -# -# The <VersionInt> of the file, or undef if the file doesn't exist. -# -sub Open #(file, hasBraceGroups) - { - my $self; - ($self, $file, $hasBraceGroups) = @_; - - @errors = ( ); - - # It will be incremented to one when the first line is read from the file. - $lineNumber = 0; - - open(CONFIG_FILEHANDLE, '<' . $file) or return undef; - - - # Get the format line. - - my ($keyword, $value, $comment) = $self->GetLine(); - - if ($keyword eq 'format') - { return NaturalDocs::Version->FromString($value); } - else - { die "The first content line in " . $file . " must be the Format: line.\n"; }; - }; - - -# -# Function: Close -# -# Closes the current configuration file. -# -sub Close - { - my $self = shift; - close(CONFIG_FILEHANDLE); - }; - - -# -# Function: GetLine -# -# Returns the next line containing content, or an empty array if none. -# -# Returns: -# -# Returns the array ( keyword, value, comment ), or an empty array if none. All tabs will be converted to spaces, and all -# whitespace will be condensed into a single space. -# -# keyword - The keyword part of the line, if any. Is converted to lowercase and doesn't include the colon. If the file supports -# brace groups, opening and closing braces will be returned as keywords. -# value - The value part of the line, minus any whitespace. Keeps its original case. -# comment - The comment following the line, if any. This includes the # symbol and a leading space if there was -# any whitespace, since it may be significant. Otherwise undef. Used for lines where the # character needs to be -# accepted as part of the value. -# -sub GetLine - { - my $self = shift; - - my ($line, $comment); - - - # Get the next line with content. - - do - { - # Get the next line. - - my $isFileLine; - - if (scalar @virtualLines) - { - $line = shift @virtualLines; - $isFileLine = 0; - } - else - { - $line = <CONFIG_FILEHANDLE>; - $lineNumber++; - - if (!defined $line) - { return ( ); }; - - ::XChomp(\$line); - - # Condense spaces and tabs into a single space. - $line =~ tr/\t / /s; - $isFileLine = 1; - }; - - - # Split off the comment. - - if ($line =~ /^(.*?)( ?#.*)$/) - { ($line, $comment) = ($1, $2); } - else - { $comment = undef; }; - - - # Split any brace groups. - - if ($isFileLine && $hasBraceGroups && $line =~ /[\{\}]/) - { - ($line, @virtualLines) = split(/([\{\}])/, $line); - - $virtualLines[-1] .= $comment; - $comment = undef; - }; - - - # Remove whitespace. - - $line =~ s/^ //; - $line =~ s/ $//; - $comment =~ s/ $//; - # We want to keep the leading space on a comment. - } - while (!$line); - - - # Process the line. - - if ($hasBraceGroups && ($line eq '{' || $line eq '}')) - { - return ($line, undef, undef); - }; - - - if ($line =~ /^([a-z0-9\ \'\/\.\-]+?) ?: ?(.*)$/i) # [CFChars] - { - my ($keyword, $value) = ($1, $2); - return (lc($keyword), $value, $comment); - } - - else - { - return (undef, $line, $comment); - }; - }; - - -# -# Function: LineNumber -# -# Returns the line number for the line last returned by <GetLine()>. -# -sub LineNumber - { return $lineNumber; }; - - - -############################################################################### -# Group: Error Functions - - -# -# Function: AddError -# -# Stores an error for the current configuration file. Will be attached to the last line read by <GetLine()>. -# -# Parameters: -# -# message - The error message. -# lineNumber - The line number to use. If not specified, it will use the line number from the last call to <GetLine()>. -# -sub AddError #(message, lineNumber) - { - my ($self, $message, $messageLineNumber) = @_; - - if (!defined $messageLineNumber) - { $messageLineNumber = $lineNumber; }; - - push @errors, $messageLineNumber, $message; - }; - - -# -# Function: ErrorCount -# -# Returns how many errors the configuration file has. -# -sub ErrorCount - { - return (scalar @errors) / 2; - }; - - -# -# Function: PrintErrorsAndAnnotateFile -# -# Prints the errors to STDERR in the standard GNU format and annotates the configuration file with them. It does *not* end -# execution. <Close()> *must* be called before this function. -# -sub PrintErrorsAndAnnotateFile - { - my ($self) = @_; - - if (scalar @errors) - { - open(CONFIG_FILEHANDLE, '<' . $file); - my @lines = <CONFIG_FILEHANDLE>; - close(CONFIG_FILEHANDLE); - - # We need to keep track of both the real and the original line numbers. The original line numbers are for matching errors in - # the errors array, and don't include any comment lines added or deleted. Line number is the current line number including - # those comment lines for sending to the display. - my $lineNumber = 1; - my $originalLineNumber = 1; - - open(CONFIG_FILEHANDLE, '>' . $file); - - # We don't want to keep the old error header, if present. - if ($lines[0] =~ /^\# There (?:is an error|are \d+ errors) in this file\./) - { - shift @lines; - $originalLineNumber++; - - # We want to drop the blank line after it as well. - if ($lines[0] eq "\n") - { - shift @lines; - $originalLineNumber++; - }; - }; - - if ($self->ErrorCount() == 1) - { - print CONFIG_FILEHANDLE - "# There is an error in this file. Search for ERROR to find it.\n\n"; - } - else - { - print CONFIG_FILEHANDLE - "# There are " . $self->ErrorCount() . " errors in this file. Search for ERROR to find them.\n\n"; - }; - - $lineNumber += 2; - - - foreach my $line (@lines) - { - while (scalar @errors && $originalLineNumber == $errors[0]) - { - my $errorLine = shift @errors; - my $errorMessage = shift @errors; - - print CONFIG_FILEHANDLE "# ERROR: " . $errorMessage . "\n"; - - # Use the GNU error format, which should make it easier to handle errors when Natural Docs is part of a build process. - # See http://www.gnu.org/prep/standards_15.html - - $errorMessage = lcfirst($errorMessage); - $errorMessage =~ s/\.$//; - - print STDERR 'NaturalDocs:' . $file . ':' . $lineNumber . ': ' . $errorMessage . "\n"; - - $lineNumber++; - }; - - # We want to remove error lines from previous runs. - if (substr($line, 0, 9) ne '# ERROR: ') - { - print CONFIG_FILEHANDLE $line; - $lineNumber++; - }; - - $originalLineNumber++; - }; - - # Clean up any remaining errors. - while (scalar @errors) - { - my $errorLine = shift @errors; - my $errorMessage = shift @errors; - - print CONFIG_FILEHANDLE "# ERROR: " . $errorMessage . "\n"; - - # Use the GNU error format, which should make it easier to handle errors when Natural Docs is part of a build process. - # See http://www.gnu.org/prep/standards_15.html - - $errorMessage = lcfirst($errorMessage); - $errorMessage =~ s/\.$//; - - print STDERR 'NaturalDocs:' . $file . ':' . $lineNumber . ': ' . $errorMessage . "\n"; - }; - - close(CONFIG_FILEHANDLE); - }; - }; - - - -############################################################################### -# Group: Misc Functions - - -# -# Function: HasOnlyCFChars -# -# Returns whether the passed string contains only <CFChars>. -# -sub HasOnlyCFChars #(string) - { - my ($self, $string) = @_; - return ($string =~ /^[a-z0-9\ \.\-\/\']*$/i); # [CFChars] - }; - - -# -# Function: CFCharNames -# -# Returns a plain-english list of <CFChars> which can be embedded in a sentence. For example, "You can only use -# [CFCharsList()] in the name. -# -sub CFCharNames - { - # [CFChars] - return 'letters, numbers, spaces, periods, dashes, slashes, and apostrophes'; - }; - - -# -# Function: Obscure -# -# Obscures the passed text so that it is not user editable and returns it. The encoding method is not secure; it is just designed -# to be fast and to discourage user editing. -# -sub Obscure #(text) - { - my ($self, $text) = @_; - - # ` is specifically chosen to encode to space because of its rarity. We don't want a trailing one to get cut off before decoding. - $text =~ tr{a-zA-Z0-9\ \\\/\.\:\_\-\`} - {pY9fGc\`R8lAoE\\uIdH6tN\/7sQjKx0B5mW\.vZ41PyFg\:CrLaO\_eUi2DhT\-nSqJkXb3MwVz\ }; - - return $text; - }; - - -# -# Function: Unobscure -# -# Restores text encoded with <Obscure()> and returns it. -# -sub Unobscure #(text) - { - my ($self, $text) = @_; - - $text =~ tr{pY9fGc\`R8lAoE\\uIdH6tN\/7sQjKx0B5mW\.vZ41PyFg\:CrLaO\_eUi2DhT\-nSqJkXb3MwVz\ } - {a-zA-Z0-9\ \\\/\.\:\_\-\`}; - - return $text; - }; - - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Constants.pm b/docs/doctool/Modules/NaturalDocs/Constants.pm deleted file mode 100644 index 91c53556..00000000 --- a/docs/doctool/Modules/NaturalDocs/Constants.pm +++ /dev/null @@ -1,229 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Constants -# -############################################################################### -# -# Constants that are used throughout the script. All are exported by default. -# -############################################################################### - -# 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::Constants; - -use vars qw(@EXPORT @ISA); -require Exporter; -@ISA = qw(Exporter); - -@EXPORT = ('REFERENCE_TEXT', 'REFERENCE_CH_CLASS', 'REFERENCE_CH_PARENT', - - 'RESOLVE_RELATIVE', 'RESOLVE_ABSOLUTE', 'RESOLVE_NOPLURAL', 'RESOLVE_NOUSING', - - 'MENU_TITLE', 'MENU_SUBTITLE', 'MENU_FILE', 'MENU_GROUP', 'MENU_TEXT', 'MENU_LINK', 'MENU_FOOTER', - 'MENU_INDEX', 'MENU_FORMAT', 'MENU_ENDOFORIGINAL', 'MENU_DATA', - - 'MENU_FILE_NOAUTOTITLE', 'MENU_GROUP_UPDATETITLES', 'MENU_GROUP_UPDATESTRUCTURE', - 'MENU_GROUP_UPDATEORDER', 'MENU_GROUP_HASENDOFORIGINAL', - 'MENU_GROUP_UNSORTED', 'MENU_GROUP_FILESSORTED', - 'MENU_GROUP_FILESANDGROUPSSORTED', 'MENU_GROUP_EVERYTHINGSORTED', - 'MENU_GROUP_ISINDEXGROUP', - - 'FILE_NEW', 'FILE_CHANGED', 'FILE_SAME', 'FILE_DOESNTEXIST', - - 'BINARY_FORMAT'); - -# -# Topic: Assumptions -# -# - No constant here will ever be zero. -# - All constants are exported by default. -# - - -############################################################################### -# Group: Virtual Types -# These are only groups of constants, but should be treated like typedefs or enums. Each one represents a distinct type and -# their values should only be one of their constants or undef. - - -# -# Constants: ReferenceType -# -# The type of a reference. -# -# REFERENCE_TEXT - The reference appears in the text of the documentation. -# REFERENCE_CH_CLASS - A class reference handled by <NaturalDocs::ClassHierarchy>. -# REFERENCE_CH_PARENT - A parent class reference handled by <NaturalDocs::ClassHierarchy>. -# -# Dependencies: -# -# - <NaturalDocs::ReferenceString->ToBinaryFile()> and <NaturalDocs::ReferenceString->FromBinaryFile()> require that -# these values fit into a UInt8, i.e. are <= 255. -# -use constant REFERENCE_TEXT => 1; -use constant REFERENCE_CH_CLASS => 2; -use constant REFERENCE_CH_PARENT => 3; - - -# -# Constants: MenuEntryType -# -# The types of entries that can appear in the menu. -# -# MENU_TITLE - The title of the menu. -# MENU_SUBTITLE - The sub-title of the menu. -# MENU_FILE - A source file, relative to the source directory. -# MENU_GROUP - A group. -# MENU_TEXT - Arbitrary text. -# MENU_LINK - A web link. -# MENU_FOOTER - Footer text. -# MENU_INDEX - An index. -# MENU_FORMAT - The version of Natural Docs the menu file was generated with. -# MENU_ENDOFORIGINAL - A dummy entry that marks where the original group content ends. This is used when automatically -# changing the groups so that the alphabetization or lack thereof can be detected without being -# affected by new entries tacked on to the end. -# MENU_DATA - Data not meant for user editing. -# -# Dependency: -# -# <PreviousMenuState.nd> depends on these values all being able to fit into a UInt8, i.e. <= 255. -# -use constant MENU_TITLE => 1; -use constant MENU_SUBTITLE => 2; -use constant MENU_FILE => 3; -use constant MENU_GROUP => 4; -use constant MENU_TEXT => 5; -use constant MENU_LINK => 6; -use constant MENU_FOOTER => 7; -use constant MENU_INDEX => 8; -use constant MENU_FORMAT => 9; -use constant MENU_ENDOFORIGINAL => 10; -use constant MENU_DATA => 11; - - -# -# Constants: FileStatus -# -# What happened to a file since Natural Docs' last execution. -# -# FILE_NEW - The file has been added since the last run. -# FILE_CHANGED - The file has been modified since the last run. -# FILE_SAME - The file hasn't been modified since the last run. -# FILE_DOESNTEXIST - The file doesn't exist, or was deleted. -# -use constant FILE_NEW => 1; -use constant FILE_CHANGED => 2; -use constant FILE_SAME => 3; -use constant FILE_DOESNTEXIST => 4; - - - -############################################################################### -# Group: Flags -# These constants can be combined with each other. - - -# -# Constants: Resolving Flags -# -# Used to influence the method of resolving references in <NaturalDocs::SymbolTable>. -# -# RESOLVE_RELATIVE - The reference text is truly relative, rather than Natural Docs' semi-relative. -# RESOLVE_ABSOLUTE - The reference text is always absolute. No local or relative references. -# RESOLVE_NOPLURAL - The reference text may not be interpreted as a plural, and thus match singular forms as well. -# RESOLVE_NOUSING - The reference text may not include "using" statements when being resolved. -# -# If neither <RESOLVE_RELATIVE> or <RESOLVE_ABSOLUTE> is specified, Natural Docs' semi-relative kicks in instead, -# which is where links are interpreted as local, then global, then relative. <RESOLVE_RELATIVE> states that links are -# local, then relative, then global. -# -# Dependencies: -# -# - <NaturalDocs::ReferenceString->ToBinaryFile()> and <NaturalDocs::ReferenceString->FromBinaryFile()> require that -# these values fit into a UInt8, i.e. are <= 255. -# -use constant RESOLVE_RELATIVE => 0x01; -use constant RESOLVE_ABSOLUTE => 0x02; -use constant RESOLVE_NOPLURAL => 0x04; -use constant RESOLVE_NOUSING => 0x08; - - -# -# Constants: Menu Entry Flags -# -# The various flags that can apply to a menu entry. You cannot mix flags of different types, since they may overlap. -# -# File Flags: -# -# MENU_FILE_NOAUTOTITLE - Whether the file is auto-titled or not. -# -# Group Flags: -# -# MENU_GROUP_UPDATETITLES - The group should have its auto-titles regenerated. -# MENU_GROUP_UPDATESTRUCTURE - The group should be checked for structural changes, such as being removed or being -# split into subgroups. -# MENU_GROUP_UPDATEORDER - The group should be resorted. -# -# MENU_GROUP_HASENDOFORIGINAL - Whether the group contains a dummy <MENU_ENDOFORIGINAL> entry. -# MENU_GROUP_ISINDEXGROUP - Whether the group is used primarily for <MENU_INDEX> entries. <MENU_TEXT> entries -# are tolerated. -# -# MENU_GROUP_UNSORTED - The group's contents are not sorted. -# MENU_GROUP_FILESSORTED - The group's files are sorted alphabetically. -# MENU_GROUP_FILESANDGROUPSSORTED - The group's files and sub-groups are sorted alphabetically. -# MENU_GROUP_EVERYTHINGSORTED - All entries in the group are sorted alphabetically. -# -use constant MENU_FILE_NOAUTOTITLE => 0x0001; - -use constant MENU_GROUP_UPDATETITLES => 0x0001; -use constant MENU_GROUP_UPDATESTRUCTURE => 0x0002; -use constant MENU_GROUP_UPDATEORDER => 0x0004; -use constant MENU_GROUP_HASENDOFORIGINAL => 0x0008; - -# This could really be a two-bit field instead of four flags, but it's not worth the effort since it's only used internally. -use constant MENU_GROUP_UNSORTED => 0x0010; -use constant MENU_GROUP_FILESSORTED => 0x0020; -use constant MENU_GROUP_FILESANDGROUPSSORTED => 0x0040; -use constant MENU_GROUP_EVERYTHINGSORTED => 0x0080; - -use constant MENU_GROUP_ISINDEXGROUP => 0x0100; - - - -############################################################################### -# Group: Other Constants - - -# -# Constant: BINARY_FORMAT -# -# An 8-bit constant that's used as the first byte of binary data files. This is used so that you can easily distinguish between -# binary and old-style text data files. It's not a character that would appear in plain text files. -# -use constant BINARY_FORMAT => pack('C', 0x06); -# Which is ACK or acknowledge in ASCII. Is the cool spade character in DOS displays. - - - -############################################################################### -# Group: Support Functions - - -# -# Function: IsClassHierarchyReference -# Returns whether the passed <ReferenceType> belongs to <NaturalDocs::ClassHierarchy>. -# -sub IsClassHierarchyReference #(reference) - { - my ($self, $reference) = @_; - return ($reference == REFERENCE_CH_CLASS || $reference == REFERENCE_CH_PARENT); - }; - - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/DefineMembers.pm b/docs/doctool/Modules/NaturalDocs/DefineMembers.pm deleted file mode 100644 index 63a7dbfe..00000000 --- a/docs/doctool/Modules/NaturalDocs/DefineMembers.pm +++ /dev/null @@ -1,100 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::DefineMembers -# -############################################################################### -# -# A custom Perl pragma to define member constants and accessors for use in Natural Docs objects while supporting inheritance. -# -# Each member will be defined as a numeric constant which should be used as that variable's index into the object arrayref. -# They will be assigned sequentially from zero, and take into account any members defined this way in parent classes. Note -# that you can *not* use multiple inheritance with this method. -# -# If a parameter ends in parenthesis, it will be generated as an accessor for the previous member. If it also starts with "Set", -# the accessor will accept a single parameter to replace the value with. If it's followed with "duparrayref", it will assume the -# parameter is either an arrayref or undef, and if the former, will duplicate it to set the value. -# -# Example: -# -# > package MyPackage; -# > -# > use NaturalDocs::DefineMembers 'VAR_A', 'VarA()', 'SetVarA()', -# > 'VAR_B', 'VarB()', -# > 'VAR_C', -# > 'VAR_D', 'VarD()', 'SetVarD() duparrayref'; -# > -# > sub SetC #(C) -# > { -# > my ($self, $c) = @_; -# > $self->[VAR_C] = $c; -# > }; -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - - -package NaturalDocs::DefineMembers; - -sub import #(member, member, ...) - { - my ($self, @parameters) = @_; - my $package = caller(); - - no strict 'refs'; - my $parent = ${$package . '::ISA'}[0]; - use strict 'refs'; - - my $memberConstant = 0; - my $lastMemberName; - - if (defined $parent && $parent->can('END_OF_MEMBERS')) - { $memberConstant = $parent->END_OF_MEMBERS(); }; - - my $code = '{ package ' . $package . ";\n"; - - foreach my $parameter (@parameters) - { - if ($parameter =~ /^(.+)\(\) *(duparrayref)?$/i) - { - my ($functionName, $pragma) = ($1, lc($2)); - - if ($functionName =~ /^Set/) - { - if ($pragma eq 'duparrayref') - { - $code .= - 'sub ' . $functionName . ' - { - if (defined $_[1]) - { $_[0]->[' . $lastMemberName . '] = [ @{$_[1]} ]; } - else - { $_[0]->[' . $lastMemberName . '] = undef; }; - };' . "\n"; - } - else - { - $code .= 'sub ' . $functionName . ' { $_[0]->[' . $lastMemberName . '] = $_[1]; };' . "\n"; - }; - } - else - { - $code .= 'sub ' . $functionName . ' { return $_[0]->[' . $lastMemberName . ']; };' . "\n"; - }; - } - else - { - $code .= 'use constant ' . $parameter . ' => ' . $memberConstant . ";\n"; - $memberConstant++; - $lastMemberName = $parameter; - }; - }; - - $code .= 'use constant END_OF_MEMBERS => ' . $memberConstant . ";\n"; - $code .= '};'; - - eval $code; - }; - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Error.pm b/docs/doctool/Modules/NaturalDocs/Error.pm deleted file mode 100644 index 080de38f..00000000 --- a/docs/doctool/Modules/NaturalDocs/Error.pm +++ /dev/null @@ -1,305 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Error -# -############################################################################### -# -# Manages all aspects of error handling in Natural Docs. -# -############################################################################### - -# 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; - -$SIG{'__DIE__'} = \&NaturalDocs::Error::CatchDeath; - - -package NaturalDocs::Error; - - -############################################################################### -# Group: Variables - - -# -# handle: FH_CRASHREPORT -# The filehandle used for generating crash reports. -# - - -# -# var: stackTrace -# The stack trace generated by <CatchDeath()>. -# -my $stackTrace; - - -# -# var: softDeath -# Whether the program exited using <SoftDeath()>. -# -my $softDeath; - - -# -# var: currentAction -# What Natural Docs was doing when it crashed. This stores strings generated by functions like <OnStartParsing()>. -# -my $currentAction; - - -############################################################################### -# Group: Functions - - -# -# Function: SoftDeath -# -# Generates a "soft" death, which means the program exits like with Perl's die(), but no crash report will be generated. -# -# Parameter: -# -# message - The error message to die with. -# -sub SoftDeath #(message) - { - my ($self, $message) = @_; - - $softDeath = 1; - if ($message !~ /\n$/) - { $message .= "\n"; }; - - die $message; - }; - - -# -# Function: OnStartParsing -# -# Called whenever <NaturalDocs::Parser> starts parsing a source file. -# -sub OnStartParsing #(FileName file) - { - my ($self, $file) = @_; - $currentAction = 'Parsing ' . $file; - }; - - -# -# Function: OnEndParsing -# -# Called whenever <NaturalDocs::Parser> is done parsing a source file. -# -sub OnEndParsing #(FileName file) - { - my ($self, $file) = @_; - $currentAction = undef; - }; - - -# -# Function: OnStartBuilding -# -# Called whenever <NaturalDocs::Builder> starts building a source file. -# -sub OnStartBuilding #(FileName file) - { - my ($self, $file) = @_; - $currentAction = 'Building ' . $file; - }; - - -# -# Function: OnEndBuilding -# -# Called whenever <NaturalDocs::Builder> is done building a source file. -# -sub OnEndBuilding #(FileName file) - { - my ($self, $file) = @_; - $currentAction = undef; - }; - - -# -# Function: HandleDeath -# -# Should be called whenever Natural Docs dies out of execution. -# -sub HandleDeath - { - my $self = shift; - - my $reason = $::EVAL_ERROR; - $reason =~ s/[\n\r]+$//; - - my $errorMessage = - "\n" - . "Natural Docs encountered the following error and was stopped:\n" - . "\n" - . " " . $reason . "\n" - . "\n" - - . "You can get help at the following web site:\n" - . "\n" - . " " . NaturalDocs::Settings->AppURL() . "\n" - . "\n"; - - if (!$softDeath) - { - my $crashReport = $self->GenerateCrashReport(); - - if ($crashReport) - { - $errorMessage .= - "If sending an error report, please include the information found in the\n" - . "following file:\n" - . "\n" - . " " . $crashReport . "\n" - . "\n"; - } - else - { - $errorMessage .= - "If sending an error report, please include the following information:\n" - . "\n" - . " Natural Docs version: " . NaturalDocs::Settings->TextAppVersion() . "\n" - . " Perl version: " . $self->PerlVersion() . " on " . $::OSNAME . "\n" - . "\n"; - }; - }; - - die $errorMessage; - }; - - -############################################################################### -# Group: Support Functions - - -# -# Function: PerlVersion -# Returns the current Perl version as a string. -# -sub PerlVersion - { - my $self = shift; - - my $perlVersion; - - if ($^V) - { $perlVersion = sprintf('%vd', $^V); } - if (!$perlVersion || substr($perlVersion, 0, 1) eq '%') - { $perlVersion = $]; }; - - return $perlVersion; - }; - - -# -# Function: GenerateCrashReport -# -# Generates a report and returns the <FileName> it's located at. Returns undef if it could not generate one. -# -sub GenerateCrashReport - { - my $self = shift; - - my $errorMessage = $::EVAL_ERROR; - $errorMessage =~ s/[\r\n]+$//; - - my $reportDirectory = NaturalDocs::Settings->ProjectDirectory(); - - if (!$reportDirectory || !-d $reportDirectory) - { return undef; }; - - my $file = NaturalDocs::File->JoinPaths($reportDirectory, 'LastCrash.txt'); - - open(FH_CRASHREPORT, '>' . $file) or return undef; - - print FH_CRASHREPORT - 'Crash Message:' . "\n\n" - . ' ' . $errorMessage . "\n\n"; - - if ($currentAction) - { - print FH_CRASHREPORT - 'Current Action:' . "\n\n" - . ' ' . $currentAction . "\n\n"; - }; - - print FH_CRASHREPORT - 'Natural Docs version ' . NaturalDocs::Settings->TextAppVersion() . "\n" - . 'Perl version ' . $self->PerlVersion . ' on ' . $::OSNAME . "\n\n" - . 'Command Line:' . "\n\n" - . ' ' . join(' ', @ARGV) . "\n\n"; - - if ($stackTrace) - { - print FH_CRASHREPORT - 'Stack Trace:' . "\n\n" - . $stackTrace; - } - else - { - print FH_CRASHREPORT - 'Stack Trace not available.' . "\n\n"; - }; - - close(FH_CRASHREPORT); - return $file; - }; - - -############################################################################### -# Group: Signal Handlers - - -# -# Function: CatchDeath -# -# Catches Perl die calls. -# -# *IMPORTANT:* This function is a signal handler and should not be called manually. Also, because of this, it does not have -# a $self parameter. -# -# Parameters: -# -# message - The error message to die with. -# -sub CatchDeath #(message) - { - # No $self because it's a signal handler. - my $message = shift; - - if (!$NaturalDocs::Error::softDeath) - { - my $i = 0; - my ($lastPackage, $lastFile, $lastLine, $lastFunction); - - while (my ($package, $file, $line, $function) = caller($i)) - { - if ($i != 0) - { $stackTrace .= ', called from' . "\n"; }; - - $stackTrace .= ' ' . $function; - - if (defined $lastLine) - { - $stackTrace .= ', line ' . $lastLine; - - if ($function !~ /^NaturalDocs::/) - { $stackTrace .= ' of ' . $lastFile; }; - }; - - ($lastPackage, $lastFile, $lastLine, $lastFunction) = ($package, $file, $line, $function); - $i++; - }; - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/File.pm b/docs/doctool/Modules/NaturalDocs/File.pm deleted file mode 100644 index f69f3b18..00000000 --- a/docs/doctool/Modules/NaturalDocs/File.pm +++ /dev/null @@ -1,521 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::File -# -############################################################################### -# -# A package to manage file access across platforms. Incorporates functions from various standard File:: packages, but more -# importantly, works around the glorious suckage present in File::Spec, at least in version 0.82 and earlier. Read the "Why oh -# why?" sections for why this package was necessary. -# -# Usage and Dependencies: -# -# - The package doesn't depend on any other Natural Docs packages and is ready to use immediately. -# -# - All functions except <CanonizePath()> assume that all parameters are canonized. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - -use File::Spec (); -use File::Path (); -use File::Copy (); - -use strict; -use integer; - -package NaturalDocs::File; - - -# -# Function: CheckCompatibility -# -# Checks if the standard packages required by this one are up to snuff and dies if they aren't. This is done because I can't -# tell which versions of File::Spec have splitpath just by the version numbers. -# -sub CheckCompatibility - { - my ($self) = @_; - - eval { - File::Spec->splitpath(''); - }; - - if ($@) - { - NaturalDocs::Error->SoftDeath("Natural Docs requires a newer version of File::Spec than you have. " - . "You must either upgrade it or upgrade Perl."); - }; - }; - - -############################################################################### -# Group: Path String Functions - - -# -# Function: CanonizePath -# -# Takes a path and returns a logically simplified version of it. -# -# Why oh why?: -# -# Because File::Spec->canonpath doesn't strip quotes on Windows. So if you pass in "a b\c" or "a b"\c, they still end up as -# different strings even though they're logically the same. -# -# It also doesn't remove things like "..", so "a/b/../c" doesn't simplify to "a/c" like it should. -# -sub CanonizePath #(path) - { - my ($self, $path) = @_; - - if ($::OSNAME eq 'MSWin32') - { - # We don't have to use a smarter algorithm for dropping quotes because they're invalid characters for actual file and - # directory names. - $path =~ s/\"//g; - }; - - $path = File::Spec->canonpath($path); - - # Condense a/b/../c into a/c. - - my $upDir = File::Spec->updir(); - if (index($path, $upDir) != -1) - { - my ($volume, $directoryString, $file) = $self->SplitPath($path); - my @directories = $self->SplitDirectories($directoryString); - - my $i = 1; - while ($i < scalar @directories) - { - if ($i > 0 && $directories[$i] eq $upDir) - { - splice(@directories, $i - 1, 2); - $i--; - } - else - { $i++; }; - }; - - $directoryString = $self->JoinDirectories(@directories); - $path = $self->JoinPath($volume, $directoryString, $file); - }; - - return $path; - }; - - -# -# Function: PathIsAbsolute -# -# Returns whether the passed path is absolute. -# -sub PathIsAbsolute #(path) - { - my ($self, $path) = @_; - return File::Spec->file_name_is_absolute($path); - }; - - -# -# Function: JoinPath -# -# Creates a path from its elements. -# -# Parameters: -# -# volume - The volume, such as the drive letter on Windows. Undef if none. -# dirString - The directory string. Create with <JoinDirectories()> if necessary. -# file - The file name, or undef if none. -# -# Returns: -# -# The joined path. -# -sub JoinPath #(volume, dirString, $file) - { - my ($self, $volume, $dirString, $file) = @_; - return File::Spec->catpath($volume, $dirString, $file); - }; - - -# -# Function: JoinPaths -# -# Joins two paths. -# -# Parameters: -# -# basePath - May be a relative path, an absolute path, or undef. -# extraPath - May be a relative path, a file, a relative path and file together, or undef. -# noFileInExtra - Set this to true if extraPath is a relative path only, and doesn't have a file. -# -# Returns: -# -# The joined path. -# -# Why oh why?: -# -# Because nothing in File::Spec will simply slap two paths together. They have to be split up for catpath/file, and rel2abs -# requires the base to be absolute. -# -sub JoinPaths #(basePath, extraPath, noFileInExtra) - { - my ($self, $basePath, $extraPath, $noFileInExtra) = @_; - - # If both are undef, it will return undef, which is what we want. - if (!defined $basePath) - { return $extraPath; } - elsif (!defined $extraPath) - { return $basePath; }; - - my ($baseVolume, $baseDirString, $baseFile) = File::Spec->splitpath($basePath, 1); - my ($extraVolume, $extraDirString, $extraFile) = File::Spec->splitpath($extraPath, $noFileInExtra); - - my @baseDirectories = $self->SplitDirectories($baseDirString); - my @extraDirectories = $self->SplitDirectories($extraDirString); - - my $fullDirString = $self->JoinDirectories(@baseDirectories, @extraDirectories); - - my $fullPath = File::Spec->catpath($baseVolume, $fullDirString, $extraFile); - - return $self->CanonizePath($fullPath); - }; - - -# -# Function: SplitPath -# -# Takes a path and returns its elements. -# -# Parameters: -# -# path - The path to split. -# noFile - Set to true if the path doesn't have a file at the end. -# -# Returns: -# -# The array ( volume, directoryString, file ). If any don't apply, they will be undef. Use <SplitDirectories()> to split the -# directory string if desired. -# -# Why oh Why?: -# -# Because File::Spec->splitpath may leave a trailing slash/backslash/whatever on the directory string, which makes -# it a bit hard to match it with results from File::Spec->catdir. -# -sub SplitPath #(path, noFile) - { - my ($self, $path, $noFile) = @_; - - my @segments = File::Spec->splitpath($path, $noFile); - - if (!length $segments[0]) - { $segments[0] = undef; }; - if (!length $segments[2]) - { $segments[2] = undef; }; - - $segments[1] = File::Spec->catdir( File::Spec->splitdir($segments[1]) ); - - return @segments; - }; - - -# -# Function: JoinDirectories -# -# Creates a directory string from an array of directory names. -# -# Parameters: -# -# directory - A directory name. There may be as many of these as desired. -# -sub JoinDirectories #(directory, directory, ...) - { - my ($self, @directories) = @_; - return File::Spec->catdir(@directories); - }; - - -# -# Function: SplitDirectories -# -# Takes a string of directories and returns an array of its elements. -# -# Why oh why?: -# -# Because File::Spec->splitdir might leave an empty element at the end of the array, which screws up both joining in -# <ConvertToURL> and navigation in <MakeRelativePath>. Morons. -# -sub SplitDirectories #(directoryString) - { - my ($self, $directoryString) = @_; - - my @directories = File::Spec->splitdir($directoryString); - - if (!length $directories[-1]) - { pop @directories; }; - - return @directories; - }; - - -# -# Function: MakeRelativePath -# -# Takes two paths and returns a relative path between them. -# -# Parameters: -# -# basePath - The starting path. May be relative or absolute, so long as the target path is as well. -# targetPath - The target path. May be relative or absolute, so long as the base path is as well. -# -# If both paths are relative, they are assumed to be relative to the same base. -# -# Returns: -# -# The target path relative to base. -# -# Why oh why?: -# -# Wow, where to begin? First of all, there's nothing that gives a relative path between two relative paths. -# -# Second of all, if target and base are absolute but on different volumes, File::Spec->abs2rel creates a totally non-functional -# relative path. It should return the target as is, since there is no relative path. -# -# Third of all, File::Spec->abs2rel between absolute paths on the same volume, at least on Windows, leaves the drive letter -# on. So abs2rel('a:\b\c\d', 'a:\b') returns 'a:c\d' instead of the expected 'c\d'. That makes no fucking sense whatsoever. It's -# not like it was designed to handle only directory names, either; the documentation says 'path' and the code seems to -# explicitly handle it. There's just an 'unless' in there that tacks on the volume, defeating the purpose of a *relative* path and -# making the function worthless. Morons. -# -# Update: This last one appears to be fixed in File::Spec 0.83, but that version isn't even listed on CPAN. Lovely. Apparently -# it just comes with ActivePerl. Somehow I don't think most Linux users are using that. -# -sub MakeRelativePath #(basePath, targetPath) - { - my ($self, $basePath, $targetPath) = @_; - - my ($baseVolume, $baseDirString, $baseFile) = $self->SplitPath($basePath, 1); - my ($targetVolume, $targetDirString, $targetFile) = $self->SplitPath($targetPath); - - # If the volumes are different, there is no possible relative path. - if ($targetVolume ne $baseVolume) - { return $targetPath; }; - - my @baseDirectories = $self->SplitDirectories($baseDirString); - my @targetDirectories = $self->SplitDirectories($targetDirString); - - # Skip the parts of the path that are the same. - while (scalar @baseDirectories && @targetDirectories && $baseDirectories[0] eq $targetDirectories[0]) - { - shift @baseDirectories; - shift @targetDirectories; - }; - - # Back out of the base path until it reaches where they were similar. - for (my $i = 0; $i < scalar @baseDirectories; $i++) - { - unshift @targetDirectories, File::Spec->updir(); - }; - - $targetDirString = $self->JoinDirectories(@targetDirectories); - - return File::Spec->catpath(undef, $targetDirString, $targetFile); - }; - - -# -# Function: IsSubPathOf -# -# Returns whether the path is a descendant of another path. -# -# Parameters: -# -# base - The base path to test against. -# path - The possible subpath to test. -# -# Returns: -# -# Whether path is a descendant of base. -# -sub IsSubPathOf #(base, path) - { - my ($self, $base, $path) = @_; - - # This is a quick test that should find a false quickly. - if ($base eq substr($path, 0, length($base))) - { - # This doesn't guarantee true, because it could be "C:\A B" and "C:\A B C\File". So we test for it by seeing if the last - # directory in base is the same as the equivalent directory in path. - - my ($baseVolume, $baseDirString, $baseFile) = NaturalDocs::File->SplitPath($base, 1); - my @baseDirectories = NaturalDocs::File->SplitDirectories($baseDirString); - - my ($pathVolume, $pathDirString, $pathFile) = NaturalDocs::File->SplitPath($path); - my @pathDirectories = NaturalDocs::File->SplitDirectories($pathDirString); - - return ( $baseDirectories[-1] eq $pathDirectories[ scalar @baseDirectories - 1 ] ); - } - else - { return undef; }; - }; - - -# -# Function: ConvertToURL -# -# Takes a relative path and converts it from the native format to a relative URL. Note that it _doesn't_ convert special characters -# to amp chars. -# -sub ConvertToURL #(path) - { - my ($self, $path) = @_; - - my ($pathVolume, $pathDirString, $pathFile) = $self->SplitPath($path); - my @pathDirectories = $self->SplitDirectories($pathDirString); - - my $i = 0; - while ($i < scalar @pathDirectories && $pathDirectories[$i] eq File::Spec->updir()) - { - $pathDirectories[$i] = '..'; - $i++; - }; - - return join('/', @pathDirectories, $pathFile); - }; - - -# -# Function: NoUpwards -# -# Takes an array of directory entries and returns one without all the entries that refer to the parent directory, such as '.' and '..'. -# -sub NoUpwards #(array) - { - my ($self, @array) = @_; - return File::Spec->no_upwards(@array); - }; - - -# -# Function: NoFileName -# -# Takes a path and returns a version without the file name. Useful for sending paths to <CreatePath()>. -# -sub NoFileName #(path) - { - my ($self, $path) = @_; - - my ($pathVolume, $pathDirString, $pathFile) = File::Spec->splitpath($path); - - return File::Spec->catpath($pathVolume, $pathDirString, undef); - }; - - -# -# Function: ExtensionOf -# -# Returns the extension of the passed path, or undef if none. -# -sub ExtensionOf #(path) - { - my ($self, $path) = @_; - - my ($pathVolume, $pathDirString, $pathFile) = File::Spec->splitpath($path); - - # We need the leading dot in the regex so files that start with a dot but don't have an extension count as extensionless files. - if ($pathFile =~ /.\.([^\.]+)$/) - { return $1; } - else - { return undef; }; - }; - - -# -# Function: IsCaseSensitive -# -# Returns whether the current platform has case-sensitive paths. -# -sub IsCaseSensitive - { - return !(File::Spec->case_tolerant()); - }; - - - -############################################################################### -# Group: Disk Functions - - -# -# Function: CreatePath -# -# Creates a directory tree corresponding to the passed path, regardless of how many directories do or do not already exist. -# Do _not_ include a file name in the path. Use <NoFileName()> first if you need to. -# -sub CreatePath #(path) - { - my ($self, $path) = @_; - File::Path::mkpath($path); - }; - - -# -# Function: RemoveEmptyTree -# -# Removes an empty directory tree. The passed directory will be removed if it's empty, and it will keep removing its parents -# until it reaches one that's not empty or a set limit. -# -# Parameters: -# -# path - The path to start from. It will try to remove this directory and work it's way down. -# limit - The path to stop at if it doesn't find any non-empty directories first. This path will *not* be removed. -# -sub RemoveEmptyTree #(path, limit) - { - my ($self, $path, $limit) = @_; - - my ($volume, $directoryString) = $self->SplitPath($path, 1); - my @directories = $self->SplitDirectories($directoryString); - - my $directory = $path; - - while (-d $directory && $directory ne $limit) - { - opendir FH_ND_FILE, $directory; - my @entries = readdir FH_ND_FILE; - closedir FH_ND_FILE; - - @entries = $self->NoUpwards(@entries); - - if (scalar @entries || !rmdir($directory)) - { last; }; - - pop @directories; - $directoryString = $self->JoinDirectories(@directories); - $directory = $self->JoinPath($volume, $directoryString); - }; - }; - - -# -# Function: Copy -# -# Copies a file from one path to another. If the destination file exists, it is overwritten. -# -# Parameters: -# -# source - The file to copy. -# destination - The destination to copy to. -# -sub Copy #(source, destination) - { - my ($self, $source, $destination) = @_; - File::Copy::copy($source, $destination); - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages.pm b/docs/doctool/Modules/NaturalDocs/Languages.pm deleted file mode 100644 index 4f29634c..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages.pm +++ /dev/null @@ -1,1471 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Languages -# -############################################################################### -# -# A package to manage all the programming languages Natural Docs supports. -# -# Usage and Dependencies: -# -# - Prior to use, <NaturalDocs::Settings> must be initialized and <Load()> must be called. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - -use Text::Wrap(); - -use NaturalDocs::Languages::Prototype; - -use NaturalDocs::Languages::Base; -use NaturalDocs::Languages::Simple; -use NaturalDocs::Languages::Advanced; - -use NaturalDocs::Languages::Perl; -use NaturalDocs::Languages::CSharp; -use NaturalDocs::Languages::ActionScript; - -use NaturalDocs::Languages::Ada; -use NaturalDocs::Languages::PLSQL; -use NaturalDocs::Languages::Pascal; -use NaturalDocs::Languages::Tcl; - -use strict; -use integer; - -package NaturalDocs::Languages; - - -############################################################################### -# Group: Variables - - -# -# handle: FH_LANGUAGES -# -# The file handle used for writing to <Languages.txt>. -# - - -# -# hash: languages -# -# A hash of all the defined languages. The keys are the all-lowercase language names, and the values are -# <NaturalDocs::Languages::Base>-derived objects. -# -my %languages; - -# -# hash: extensions -# -# A hash of all the defined languages' extensions. The keys are the all-lowercase extensions, and the values are the -# all-lowercase names of the languages that defined them. -# -my %extensions; - -# -# hash: shebangStrings -# -# A hash of all the defined languages' strings to search for in the shebang (#!) line. The keys are the all-lowercase strings, and -# the values are the all-lowercase names of the languages that defined them. -# -my %shebangStrings; - -# -# hash: shebangFiles -# -# A hash of all the defined languages for files where it needs to be found via shebang strings. The keys are the file names, -# and the values are language names, or undef if the file isn't supported. These values should be filled in the first time -# each file is parsed for a shebang string so that it doesn't have to be done multiple times. -# -my %shebangFiles; - -# -# array: mainLanguageNames -# -# An array of the language names that are defined in the main <Languages.txt>. -# -my @mainLanguageNames; - - - -############################################################################### -# Group: Files - - -# -# File: Languages.txt -# -# The configuration file that defines or overrides the language definitions for Natural Docs. One version sits in Natural Docs' -# configuration directory, and another can be in a project directory to add to or override them. -# -# > # [comments] -# -# Everything after a # symbol is ignored. However, for this particular file, comments can only appear on their own lines. -# They cannot appear after content on the same line. -# -# > Format: [version] -# -# Specifies the file format version of the file. -# -# -# Sections: -# -# > Ignore[d] Extension[s]: [extension] [extension] ... -# -# Causes the listed file extensions to be ignored, even if they were previously defined to be part of a language. The list is -# space-separated. ex. "Ignore Extensions: cvs txt" -# -# -# > Language: [name] -# -# Creates a new language. Everything underneath applies to this language until the next one. Names can use any -# characters. -# -# The languages "Text File" and "Shebang Script" have special meanings. Text files are considered all comment and don't -# have comment symbols. Shebang scripts have their language determined by the shebang string and automatically -# include files with no extension in addition to the extensions defined. -# -# If "Text File" doesn't define ignored prefixes, a package separator, or enum value behavior, those settings will be copied -# from the language with the most files in the source tree. -# -# -# > Alter Language: [name] -# -# Alters an existing language. Everything underneath it overrides the previous settings until the next one. Note that if a -# property has an [Add/Replace] form and that property has already been defined, you have to specify whether you're adding -# to or replacing the defined list. -# -# -# Language Properties: -# -# > Extension[s]: [extension] [extension] ... -# > [Add/Replace] Extension[s]: ... -# -# Defines file extensions for the language's source files. The list is space-separated. ex. "Extensions: c cpp". You can use -# extensions that were previously used by another language to redefine them. -# -# -# > Shebang String[s]: [string] [string] ... -# > [Add/Replace] Shebang String[s]: ... -# -# Defines a list of strings that can appear in the shebang (#!) line to designate that it's part of this language. They can -# appear anywhere in the line, so "php" will work for "#!/user/bin/php4". You can use strings that were previously used by -# another language to redefine them. -# -# -# > Ignore[d] Prefix[es] in Index: [prefix] [prefix] ... -# > Ignore[d] [Topic Type] Prefix[es] in Index: [prefix] [prefix] ... -# > [Add/Replace] Ignore[d] Prefix[es] in Index: ... -# > [Add/Replace] Ignore[d] [Topic Type] Prefix[es] in Index: ... -# -# Specifies prefixes that should be ignored when sorting symbols for an index. Can be specified in general or for a specific -# <TopicType>. The prefixes will still appear, the symbols will just be sorted as if they're not there. For example, specifying -# "ADO_" for functions will mean that "ADO_DoSomething" will appear under D instead of A. -# -# -# Basic Language Support Properties: -# -# These attributes are only available for languages with basic language support. -# -# -# > Line Comment[s]: [symbol] [symbol] ... -# -# Defines a space-separated list of symbols that are used for line comments, if any. ex. "Line Comment: //". -# -# -# > Block Comment[s]: [opening symbol] [closing symbol] [opening symbol] [closing symbol] ... -# -# Defines a space-separated list of symbol pairs that are used for block comments, if any. ex. "Block Comment: /* */". -# -# -# > Package Separator: [symbol] -# -# Defines the default package separator symbol, such as . or ::. This is for presentation only and will not affect how -# Natural Docs links are parsed. The default is a dot. -# -# -# > [Topic Type] Prototype Ender[s]: [symbol] [symbol] ... -# -# When defined, Natural Docs will attempt to collect prototypes from the code following the specified <TopicType>. It grabs -# code until the first ender symbol or the next Natural Docs comment, and if it contains the topic name, it serves as its -# prototype. Use \n to specify a line break. ex. "Function Prototype Enders: { ;", "Variable Prototype Enders: = ;". -# -# -# > Line Extender: [symbol] -# -# Defines the symbol that allows a prototype to span multiple lines if normally a line break would end it. -# -# -# > Enum Values: [global|under type|under parent] -# -# Defines how enum values are referenced. The default is global. -# -# global - Values are always global, referenced as 'value'. -# under type - Values are under the enum type, referenced as 'package.enum.value'. -# under parent - Values are under the enum's parent, referenced as 'package.value'. -# -# -# > Perl Package: [perl package] -# -# Specifies the Perl package used to fine-tune the language behavior in ways too complex to do in this file. -# -# -# Full Language Support Properties: -# -# These attributes are only available for languages with full language support. -# -# -# > Full Language Support: [perl package] -# -# Specifies the Perl package that has the parsing routines necessary for full language support. -# -# -# Revisions: -# -# 1.32: -# -# - Package Separator is now a basic language support only property. -# - Added Enum Values setting. -# -# 1.3: -# -# - The file was introduced. - - -############################################################################### -# Group: File Functions - - -# -# Function: Load -# -# Loads both the master and the project version of <Languages.txt>. -# -sub Load - { - my $self = shift; - - # Hashrefs where the keys are all-lowercase extensions/shebang strings, and the values are arrayrefs of the languages - # that defined them, earliest first, all lowercase. - my %tempExtensions; - my %tempShebangStrings; - - $self->LoadFile(1, \%tempExtensions, \%tempShebangStrings); # Main - - if (!exists $languages{'shebang script'}) - { NaturalDocs::ConfigFile->AddError('You must define "Shebang Script" in the main languages file.'); }; - if (!exists $languages{'text file'}) - { NaturalDocs::ConfigFile->AddError('You must define "Text File" in the main languages file.'); }; - - my $errorCount = NaturalDocs::ConfigFile->ErrorCount(); - - if ($errorCount) - { - NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile(); - NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors') - . ' in ' . NaturalDocs::Project->MainLanguagesFile()); - } - - - $self->LoadFile(0, \%tempExtensions, \%tempShebangStrings); # User - - $errorCount = NaturalDocs::ConfigFile->ErrorCount(); - - if ($errorCount) - { - NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile(); - NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors') - . ' in ' . NaturalDocs::Project->UserLanguagesFile()); - }; - - - # Convert the temp hashes into the real ones. - - while (my ($extension, $languages) = each %tempExtensions) - { - $extensions{$extension} = $languages->[-1]; - }; - while (my ($shebangString, $languages) = each %tempShebangStrings) - { - $shebangStrings{$shebangString} = $languages->[-1]; - }; - }; - - -# -# Function: LoadFile -# -# Loads a particular version of <Languages.txt>. -# -# Parameters: -# -# isMain - Whether the file is the main file or not. -# tempExtensions - A hashref where the keys are all-lowercase extensions, and the values are arrayrefs of the all-lowercase -# names of the languages that defined them, earliest first. It will be changed by this function. -# tempShebangStrings - A hashref where the keys are all-lowercase shebang strings, and the values are arrayrefs of the -# all-lowercase names of the languages that defined them, earliest first. It will be changed by this -# function. -# -sub LoadFile #(isMain, tempExtensions, tempShebangStrings) - { - my ($self, $isMain, $tempExtensions, $tempShebangStrings) = @_; - - my ($file, $status); - - if ($isMain) - { - $file = NaturalDocs::Project->MainLanguagesFile(); - $status = NaturalDocs::Project->MainLanguagesFileStatus(); - } - else - { - $file = NaturalDocs::Project->UserLanguagesFile(); - $status = NaturalDocs::Project->UserLanguagesFileStatus(); - }; - - - my $version; - - # An array of properties for the current language. Each entry is the three consecutive values ( lineNumber, keyword, value ). - my @properties; - - if ($version = NaturalDocs::ConfigFile->Open($file)) - { - # The format hasn't changed significantly since the file was introduced. - - if ($status == ::FILE_CHANGED()) - { - NaturalDocs::Project->ReparseEverything(); - NaturalDocs::SymbolTable->RebuildAllIndexes(); # Because the ignored prefixes could change. - }; - - my ($keyword, $value, $comment); - - while (($keyword, $value, $comment) = NaturalDocs::ConfigFile->GetLine()) - { - $value .= $comment; - $value =~ s/^ //; - - # Process previous properties. - if (($keyword eq 'language' || $keyword eq 'alter language') && scalar @properties) - { - if ($isMain && $properties[1] eq 'language') - { push @mainLanguageNames, $properties[2]; }; - - $self->ProcessProperties(\@properties, $version, $tempExtensions, $tempShebangStrings); - @properties = ( ); - }; - - if ($keyword =~ /^ignored? extensions?$/) - { - $value =~ tr/.*//d; - my @extensions = split(/ /, lc($value)); - - foreach my $extension (@extensions) - { delete $tempExtensions->{$extension}; }; - } - else - { - push @properties, NaturalDocs::ConfigFile->LineNumber(), $keyword, $value; - }; - }; - - if (scalar @properties) - { - if ($isMain && $properties[1] eq 'language') - { push @mainLanguageNames, $properties[2]; }; - - $self->ProcessProperties(\@properties, $version, $tempExtensions, $tempShebangStrings); - }; - } - - else # couldn't open file - { - if ($isMain) - { die "Couldn't open languages file " . $file . "\n"; }; - }; - }; - - -# -# Function: ProcessProperties -# -# Processes an array of language properties from <Languages.txt>. -# -# Parameters: -# -# properties - An arrayref of properties where each entry is the three consecutive values ( lineNumber, keyword, value ). -# It must start with the Language or Alter Language property. -# version - The <VersionInt> of the file. -# tempExtensions - A hashref where the keys are all-lowercase extensions, and the values are arrayrefs of the all-lowercase -# names of the languages that defined them, earliest first. It will be changed by this function. -# tempShebangStrings - A hashref where the keys are all-lowercase shebang strings, and the values are arrayrefs of the -# all-lowercase names of the languages that defined them, earliest first. It will be changed by this -# function. -# -sub ProcessProperties #(properties, version, tempExtensions, tempShebangStrings) - { - my ($self, $properties, $version, $tempExtensions, $tempShebangStrings) = @_; - - - # First validate the name and check whether the language has full support. - - my $language; - my $fullLanguageSupport; - my ($lineNumber, $languageKeyword, $languageName) = @$properties[0..2]; - my $lcLanguageName = lc($languageName); - my ($keyword, $value); - - if ($languageKeyword eq 'alter language') - { - $language = $languages{$lcLanguageName}; - - if (!defined $language) - { - NaturalDocs::ConfigFile->AddError('The language ' . $languageName . ' is not defined.', $lineNumber); - return; - } - else - { - $fullLanguageSupport = (!$language->isa('NaturalDocs::Languages::Simple')); - }; - } - - elsif ($languageKeyword eq 'language') - { - if (exists $languages{$lcLanguageName}) - { - NaturalDocs::ConfigFile->AddError('The language ' . $value . ' is already defined. Use "Alter Language" if you want ' - . 'to override its settings.', $lineNumber); - return; - }; - - # Case is important with these two. - if ($lcLanguageName eq 'shebang script') - { $languageName = 'Shebang Script'; } - elsif ($lcLanguageName eq 'text file') - { $languageName = 'Text File'; }; - - - # Go through the properties looking for whether the language has basic or full support and which package to use to create - # it. - - for (my $i = 3; $i < scalar @$properties; $i += 3) - { - ($lineNumber, $keyword, $value) = @$properties[$i..$i+2]; - - if ($keyword eq 'full language support') - { - $fullLanguageSupport = 1; - - eval - { - $language = $value->New($languageName); - }; - if ($::EVAL_ERROR) - { - NaturalDocs::ConfigFile->AddError('Could not create ' . $value . ' object.', $lineNumber); - return; - }; - - last; - } - - elsif ($keyword eq 'perl package') - { - eval - { - $language = $value->New($languageName); - }; - if ($::EVAL_ERROR) - { - NaturalDocs::ConfigFile->AddError('Could not create ' . $value . ' object.', $lineNumber); - return; - }; - }; - }; - - # If $language was not created by now, it's a generic basic support language. - if (!defined $language) - { $language = NaturalDocs::Languages::Simple->New($languageName); }; - - $languages{$lcLanguageName} = $language; - } - - else # not language or alter language - { - NaturalDocs::ConfigFile->AddError('You must start this line with "Language", "Alter Language", or "Ignore Extensions".', - $lineNumber); - return; - }; - - - # Decode the properties. - - for (my $i = 3; $i < scalar @$properties; $i += 3) - { - ($lineNumber, $keyword, $value) = @$properties[$i..$i+2]; - - if ($keyword =~ /^(?:(add|replace) )?extensions?$/) - { - my $command = $1; - - - # Remove old extensions. - - if (defined $language->Extensions() && $command eq 'replace') - { - foreach my $extension (@{$language->Extensions()}) - { - if (exists $tempExtensions->{$extension}) - { - my $languages = $tempExtensions->{$extension}; - my $i = 0; - - while ($i < scalar @$languages) - { - if ($languages->[$i] eq $lcLanguageName) - { splice(@$languages, $i, 1); } - else - { $i++; }; - }; - - if (!scalar @$languages) - { delete $tempExtensions->{$extension}; }; - }; - }; - }; - - - # Add new extensions. - - # Ignore stars and dots so people could use .ext or *.ext. - $value =~ s/\*\.|\.//g; - - my @extensions = split(/ /, lc($value)); - - foreach my $extension (@extensions) - { - if (!exists $tempExtensions->{$extension}) - { $tempExtensions->{$extension} = [ ]; }; - - push @{$tempExtensions->{$extension}}, $lcLanguageName; - }; - - - # Set the extensions for the language object. - - if (defined $language->Extensions()) - { - if ($command eq 'add') - { push @extensions, @{$language->Extensions()}; } - elsif (!$command) - { - NaturalDocs::ConfigFile->AddError('You need to specify whether you are adding to or replacing the list of extensions.', - $lineNumber); - }; - }; - - $language->SetExtensions(\@extensions); - } - - elsif ($keyword =~ /^(?:(add|replace) )?shebang strings?$/) - { - my $command = $1; - - - # Remove old strings. - - if (defined $language->ShebangStrings() && $command eq 'replace') - { - foreach my $shebangString (@{$language->ShebangStrings()}) - { - if (exists $tempShebangStrings->{$shebangString}) - { - my $languages = $tempShebangStrings->{$shebangString}; - my $i = 0; - - while ($i < scalar @$languages) - { - if ($languages->[$i] eq $lcLanguageName) - { splice(@$languages, $i, 1); } - else - { $i++; }; - }; - - if (!scalar @$languages) - { delete $tempShebangStrings->{$shebangString}; }; - }; - }; - }; - - - # Add new strings. - - my @shebangStrings = split(/ /, lc($value)); - - foreach my $shebangString (@shebangStrings) - { - if (!exists $tempShebangStrings->{$shebangString}) - { $tempShebangStrings->{$shebangString} = [ ]; }; - - push @{$tempShebangStrings->{$shebangString}}, $lcLanguageName; - }; - - - # Set the strings for the language object. - - if (defined $language->ShebangStrings()) - { - if ($command eq 'add') - { push @shebangStrings, @{$language->ShebangStrings()}; } - elsif (!$command) - { - NaturalDocs::ConfigFile->AddError('You need to specify whether you are adding to or replacing the list of shebang ' - . 'strings.', $lineNumber); - }; - }; - - $language->SetShebangStrings(\@shebangStrings); - } - - elsif ($keyword eq 'package separator') - { - if ($fullLanguageSupport) - { - # Prior to 1.32, package separator was used with full language support too. Accept it without complaining, even though - # we ignore it. - if ($version >= NaturalDocs::Version->FromString('1.32')) - { - NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber); - }; - } - else - { $language->SetPackageSeparator($value); }; - } - - elsif ($keyword =~ /^(?:(add|replace) )?ignored? (?:(.+) )?prefix(?:es)? in index$/) - { - my ($command, $topicName) = ($1, $2); - my $topicType; - - if ($topicName) - { - if (!( ($topicType, undef) = NaturalDocs::Topics->NameInfo($topicName) )) - { - NaturalDocs::ConfigFile->AddError($topicName . ' is not a defined topic type.', $lineNumber); - }; - } - else - { $topicType = ::TOPIC_GENERAL(); }; - - if ($topicType) - { - my @prefixes; - - if (defined $language->IgnoredPrefixesFor($topicType)) - { - if ($command eq 'add') - { @prefixes = @{$language->IgnoredPrefixesFor($topicType)}; } - elsif (!$command) - { - NaturalDocs::ConfigFile->AddError('You need to specify whether you are adding to or replacing the list of ' - . 'ignored prefixes.', $lineNumber); - }; - }; - - push @prefixes, split(/ /, $value); - $language->SetIgnoredPrefixesFor($topicType, \@prefixes); - }; - } - - elsif ($keyword eq 'full language support' || $keyword eq 'perl package') - { - if ($languageKeyword eq 'alter language') - { - NaturalDocs::ConfigFile->AddError('You cannot use ' . $keyword . ' with Alter Language.', $lineNumber); - }; - # else ignore it. - } - - elsif ($keyword =~ /^line comments?$/) - { - if ($fullLanguageSupport) - { - NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber); - } - else - { - my @symbols = split(/ /, $value); - $language->SetLineCommentSymbols(\@symbols); - }; - } - - elsif ($keyword =~ /^block comments?$/) - { - if ($fullLanguageSupport) - { - NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber); - } - else - { - my @symbols = split(/ /, $value); - - if ((scalar @symbols) % 2 == 0) - { $language->SetBlockCommentSymbols(\@symbols); } - else - { NaturalDocs::ConfigFile->AddError('Block comment symbols must appear in pairs.', $lineNumber); }; - }; - } - - elsif ($keyword =~ /^(?:(.+) )?prototype enders?$/) - { - if ($fullLanguageSupport) - { - NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber); - } - else - { - my $topicName = $1; - my $topicType; - - if ($topicName) - { - if (!( ($topicType, undef) = NaturalDocs::Topics->NameInfo($topicName) )) - { - NaturalDocs::ConfigFile->AddError($topicName . ' is not a defined topic type.', $lineNumber); - }; - } - else - { $topicType = ::TOPIC_GENERAL(); }; - - if ($topicType) - { - $value =~ s/\\n/\n/g; - my @symbols = split(/ /, $value); - $language->SetPrototypeEndersFor($topicType, \@symbols); - }; - }; - } - - elsif ($keyword eq 'line extender') - { - if ($fullLanguageSupport) - { - NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber); - } - else - { - $language->SetLineExtender($value); - }; - } - - elsif ($keyword eq 'enum values') - { - if ($fullLanguageSupport) - { - NaturalDocs::ConfigFile->AddError('You cannot define this property when using full language support.', $lineNumber); - } - else - { - $value = lc($value); - my $constant; - - if ($value eq 'global') - { $constant = ::ENUM_GLOBAL(); } - elsif ($value eq 'under type') - { $constant = ::ENUM_UNDER_TYPE(); } - elsif ($value eq 'under parent') - { $constant = ::ENUM_UNDER_PARENT(); }; - - if (defined $value) - { $language->SetEnumValues($constant); } - else - { - NaturalDocs::ConfigFile->AddError('Enum Values must be "Global", "Under Type", or "Under Parent".', $lineNumber); - }; - }; - } - - else - { - NaturalDocs::ConfigFile->AddError($keyword . ' is not a valid keyword.', $lineNumber); - }; - }; - }; - - -# -# Function: Save -# -# Saves the main and user versions of <Languages.txt>. -# -sub Save - { - my $self = shift; - - $self->SaveFile(1); # Main - $self->SaveFile(0); # User - }; - - -# -# Function: SaveFile -# -# Saves a particular version of <Topics.txt>. -# -# Parameters: -# -# isMain - Whether the file is the main file or not. -# -sub SaveFile #(isMain) - { - my ($self, $isMain) = @_; - - my $file; - - if ($isMain) - { - if (NaturalDocs::Project->MainLanguagesFileStatus() == ::FILE_SAME()) - { return; }; - $file = NaturalDocs::Project->MainLanguagesFile(); - } - else - { - # Have to check the main too because this file lists the languages defined there. - if (NaturalDocs::Project->UserLanguagesFileStatus() == ::FILE_SAME() && - NaturalDocs::Project->MainLanguagesFileStatus() == ::FILE_SAME()) - { return; }; - $file = NaturalDocs::Project->UserLanguagesFile(); - }; - - - # Array of segments, with each being groups of three consecutive entries. The first is the keyword ('language' or - # 'alter language'), the second is the value, and the third is a hashref of all the properties. - # - For properties that can accept a topic type, the property values are hashrefs mapping topic types to the values. - # - For properties that can accept 'add' or 'replace', there is an additional property ending in 'command' that stores it. - # - For properties that can accept both, the 'command' thing is applied to the topic types rather than the properties. - my @segments; - - my @ignoredExtensions; - - my $currentProperties; - my $version; - - if ($version = NaturalDocs::ConfigFile->Open($file)) - { - # We can assume the file is valid. - - while (my ($keyword, $value, $comment) = NaturalDocs::ConfigFile->GetLine()) - { - $value .= $comment; - $value =~ s/^ //; - - if ($keyword eq 'language') - { - $currentProperties = { }; - - # Case is important with these two. - if (lc($value) eq 'shebang script') - { $value = 'Shebang Script'; } - elsif (lc($value) eq 'text file') - { $value = 'Text File'; }; - - push @segments, 'language', $value, $currentProperties; - } - - elsif ($keyword eq 'alter language') - { - $currentProperties = { }; - push @segments, 'alter language', $languages{lc($value)}->Name(), $currentProperties; - } - - elsif ($keyword =~ /^ignored? extensions?$/) - { - $value =~ tr/*.//d; - push @ignoredExtensions, split(/ /, $value); - } - - elsif ($keyword eq 'package separator' || $keyword eq 'full language support' || $keyword eq 'perl package' || - $keyword eq 'line extender' || $keyword eq 'enum values') - { - $currentProperties->{$keyword} = $value; - } - - elsif ($keyword =~ /^line comments?$/) - { - $currentProperties->{'line comments'} = $value; - } - elsif ($keyword =~ /^block comments?$/) - { - $currentProperties->{'block comments'} = $value; - } - - elsif ($keyword =~ /^(?:(add|replace) )?extensions?$/) - { - my $command = $1; - - if ($command eq 'add' && exists $currentProperties->{'extensions'}) - { $currentProperties->{'extensions'} .= ' ' . $value; } - else - { - $currentProperties->{'extensions'} = $value; - $currentProperties->{'extensions command'} = $command; - }; - } - - elsif ($keyword =~ /^(?:(add|replace) )?shebang strings?$/) - { - my $command = $1; - - if ($command eq 'add' && exists $currentProperties->{'shebang strings'}) - { $currentProperties->{'shebang strings'} .= ' ' . $value; } - else - { - $currentProperties->{'shebang strings'} = $value; - $currentProperties->{'shebang strings command'} = $command; - }; - } - - elsif ($keyword =~ /^(?:(.+) )?prototype enders?$/) - { - my $topicName = $1; - my $topicType; - - if ($topicName) - { ($topicType, undef) = NaturalDocs::Topics->NameInfo($topicName); } - else - { $topicType = ::TOPIC_GENERAL(); }; - - my $currentTypeProperties = $currentProperties->{'prototype enders'}; - - if (!defined $currentTypeProperties) - { - $currentTypeProperties = { }; - $currentProperties->{'prototype enders'} = $currentTypeProperties; - }; - - $currentTypeProperties->{$topicType} = $value; - } - - elsif ($keyword =~ /^(?:(add|replace) )?ignored? (?:(.+) )?prefix(?:es)? in index$/) - { - my ($command, $topicName) = ($1, $2); - my $topicType; - - if ($topicName) - { ($topicType, undef) = NaturalDocs::Topics->NameInfo($topicName); } - else - { $topicType = ::TOPIC_GENERAL(); }; - - my $currentTypeProperties = $currentProperties->{'ignored prefixes in index'}; - - if (!defined $currentTypeProperties) - { - $currentTypeProperties = { }; - $currentProperties->{'ignored prefixes in index'} = $currentTypeProperties; - }; - - if ($command eq 'add' && exists $currentTypeProperties->{$topicType}) - { $currentTypeProperties->{$topicType} .= ' ' . $value; } - else - { - $currentTypeProperties->{$topicType} = $value; - $currentTypeProperties->{$topicType . ' command'} = $command; - }; - }; - }; - - NaturalDocs::ConfigFile->Close(); - }; - - - if (!open(FH_LANGUAGES, '>' . $file)) - { - # The main file may be on a shared volume or some other place the user doesn't have write access to. Since this is only to - # reformat the file, we can ignore the failure. - if ($isMain) - { return; } - else - { die "Couldn't save " . $file; }; - }; - - print FH_LANGUAGES 'Format: ' . NaturalDocs::Settings->TextAppVersion() . "\n\n"; - - # Remember the 80 character limit. - - if ($isMain) - { - print FH_LANGUAGES - "# This is the main Natural Docs languages file. If you change anything here,\n" - . "# it will apply to EVERY PROJECT you use Natural Docs on. If you'd like to\n" - . "# change something for just one project, edit the Languages.txt in its project\n" - . "# directory instead.\n"; - } - else - { - print FH_LANGUAGES - "# This is the Natural Docs languages file for this project. If you change\n" - . "# anything here, it will apply to THIS PROJECT ONLY. If you'd like to change\n" - . "# something for all your projects, edit the Languages.txt in Natural Docs'\n" - . "# Config directory instead.\n\n\n"; - - if (scalar @ignoredExtensions == 1) - { - print FH_LANGUAGES - 'Ignore Extension: ' . $ignoredExtensions[0] . "\n"; - } - elsif (scalar @ignoredExtensions) - { - print FH_LANGUAGES - 'Ignore Extensions: ' . join(' ', @ignoredExtensions) . "\n"; - } - else - { - print FH_LANGUAGES - "# You can prevent certain file extensions from being scanned like this:\n" - . "# Ignore Extensions: [extension] [extension] ...\n" - }; - }; - - print FH_LANGUAGES - "\n\n" - . "#-------------------------------------------------------------------------------\n" - . "# SYNTAX:\n" - . "#\n" - . "# Unlike other Natural Docs configuration files, in this file all comments\n" - . "# MUST be alone on a line. Some languages deal with the # character, so you\n" - . "# cannot put comments on the same line as content.\n" - . "#\n" - . "# Also, all lists are separated with spaces, not commas, again because some\n" - . "# languages may need to use them.\n" - . "#\n"; - - if ($isMain) - { - print FH_LANGUAGES - "# Language: [name]\n" - . "# Defines a new language. Its name can use any characters.\n" - . "#\n"; - } - else - { - print FH_LANGUAGES - "# Language: [name]\n" - . "# Alter Language: [name]\n" - . "# Defines a new language or alters an existing one. Its name can use any\n" - . "# characters. If any of the properties below have an add/replace form, you\n" - . "# must use that when using Alter Language.\n" - . "#\n"; - }; - - print FH_LANGUAGES - "# The language Shebang Script is special. It's entry is only used for\n" - . "# extensions, and files with those extensions have their shebang (#!) lines\n" - . "# read to determine the real language of the file. Extensionless files are\n" - . "# always treated this way.\n" - . "#\n" - . "# The language Text File is also special. It's treated as one big comment\n" - . "# so you can put Natural Docs content in them without special symbols. Also,\n" - . "# if you don't specify a package separator, ignored prefixes, or enum value\n" - . "# behavior, it will copy those settings from the language that is used most\n" - . "# in the source tree.\n" - . "#\n" - . "# Extensions: [extension] [extension] ...\n"; - - if ($isMain) - { - print FH_LANGUAGES - "# Defines the file extensions of the language's source files. You can use *\n" - . "# to mean any undefined extension.\n" - . "#\n" - . "# Shebang Strings: [string] [string] ...\n" - . "# Defines a list of strings that can appear in the shebang (#!) line to\n" - . "# designate that it's part of the language.\n" - . "#\n"; - } - else - { - print FH_LANGUAGES - "# [Add/Replace] Extensions: [extension] [extension] ...\n" - . "# Defines the file extensions of the language's source files. You can\n" - . "# redefine extensions found in the main languages file. You can use * to\n" - . "# mean any undefined extension.\n" - . "#\n" - . "# Shebang Strings: [string] [string] ...\n" - . "# [Add/Replace] Shebang Strings: [string] [string] ...\n" - . "# Defines a list of strings that can appear in the shebang (#!) line to\n" - . "# designate that it's part of the language. You can redefine strings found\n" - . "# in the main languages file.\n" - . "#\n"; - }; - - print FH_LANGUAGES - "# Ignore Prefixes in Index: [prefix] [prefix] ...\n" - . (!$isMain ? "# [Add/Replace] Ignored Prefixes in Index: [prefix] [prefix] ...\n#\n" : '') - . "# Ignore [Topic Type] Prefixes in Index: [prefix] [prefix] ...\n" - . (!$isMain ? "# [Add/Replace] Ignored [Topic Type] Prefixes in Index: [prefix] [prefix] ...\n" : '') - . "# Specifies prefixes that should be ignored when sorting symbols in an\n" - . "# index. Can be specified in general or for a specific topic type.\n" - . "#\n" - . "#------------------------------------------------------------------------------\n" - . "# For basic language support only:\n" - . "#\n" - . "# Line Comments: [symbol] [symbol] ...\n" - . "# Defines a space-separated list of symbols that are used for line comments,\n" - . "# if any.\n" - . "#\n" - . "# Block Comments: [opening sym] [closing sym] [opening sym] [closing sym] ...\n" - . "# Defines a space-separated list of symbol pairs that are used for block\n" - . "# comments, if any.\n" - . "#\n" - . "# Package Separator: [symbol]\n" - . "# Defines the default package separator symbol. The default is a dot.\n" - . "#\n" - . "# [Topic Type] Prototype Enders: [symbol] [symbol] ...\n" - . "# When defined, Natural Docs will attempt to get a prototype from the code\n" - . "# immediately following the topic type. It stops when it reaches one of\n" - . "# these symbols. Use \\n for line breaks.\n" - . "#\n" - . "# Line Extender: [symbol]\n" - . "# Defines the symbol that allows a prototype to span multiple lines if\n" - . "# normally a line break would end it.\n" - . "#\n" - . "# Enum Values: [global|under type|under parent]\n" - . "# Defines how enum values are referenced. The default is global.\n" - . "# global - Values are always global, referenced as 'value'.\n" - . "# under type - Values are under the enum type, referenced as\n" - . "# 'package.enum.value'.\n" - . "# under parent - Values are under the enum's parent, referenced as\n" - . "# 'package.value'.\n" - . "#\n" - . "# Perl Package: [perl package]\n" - . "# Specifies the Perl package used to fine-tune the language behavior in ways\n" - . "# too complex to do in this file.\n" - . "#\n" - . "#------------------------------------------------------------------------------\n" - . "# For full language support only:\n" - . "#\n" - . "# Full Language Support: [perl package]\n" - . "# Specifies the Perl package that has the parsing routines necessary for full\n" - . "# language support.\n" - . "#\n" - . "#-------------------------------------------------------------------------------\n\n"; - - if ($isMain) - { - print FH_LANGUAGES - "# The following languages MUST be defined in this file:\n" - . "#\n" - . "# Text File, Shebang Script\n"; - } - else - { - print FH_LANGUAGES - "# The following languages are defined in the main file, if you'd like to alter\n" - . "# them:\n" - . "#\n" - . Text::Wrap::wrap('# ', '# ', join(', ', @mainLanguageNames)) . "\n"; - }; - - print FH_LANGUAGES "\n" - . "# If you add a language that you think would be useful to other developers\n" - . "# and should be included in Natural Docs by default, please e-mail it to\n" - . "# languages [at] naturaldocs [dot] org.\n"; - - my @topicTypeOrder = ( ::TOPIC_GENERAL(), ::TOPIC_CLASS(), ::TOPIC_FUNCTION(), ::TOPIC_VARIABLE(), - ::TOPIC_PROPERTY(), ::TOPIC_TYPE(), ::TOPIC_CONSTANT() ); - - for (my $i = 0; $i < scalar @segments; $i += 3) - { - my ($keyword, $name, $properties) = @segments[$i..$i+2]; - - print FH_LANGUAGES "\n\n"; - - if ($keyword eq 'language') - { print FH_LANGUAGES 'Language: ' . $name . "\n\n"; } - else - { print FH_LANGUAGES 'Alter Language: ' . $name . "\n\n"; }; - - if (exists $properties->{'extensions'}) - { - print FH_LANGUAGES ' '; - - if ($properties->{'extensions command'}) - { print FH_LANGUAGES ucfirst($properties->{'extensions command'}) . ' '; }; - - my @extensions = split(/ /, $properties->{'extensions'}, 2); - - if (scalar @extensions == 1) - { print FH_LANGUAGES 'Extension: '; } - else - { print FH_LANGUAGES 'Extensions: '; }; - - print FH_LANGUAGES lc($properties->{'extensions'}) . "\n"; - }; - - if (exists $properties->{'shebang strings'}) - { - print FH_LANGUAGES ' '; - - if ($properties->{'shebang strings command'}) - { print FH_LANGUAGES ucfirst($properties->{'shebang strings command'}) . ' '; }; - - my @shebangStrings = split(/ /, $properties->{'shebang strings'}, 2); - - if (scalar @shebangStrings == 1) - { print FH_LANGUAGES 'Shebang String: '; } - else - { print FH_LANGUAGES 'Shebang Strings: '; }; - - print FH_LANGUAGES lc($properties->{'shebang strings'}) . "\n"; - }; - - if (exists $properties->{'ignored prefixes in index'}) - { - my $topicTypePrefixes = $properties->{'ignored prefixes in index'}; - - my %usedTopicTypes; - my @topicTypes = ( @topicTypeOrder, keys %$topicTypePrefixes ); - - foreach my $topicType (@topicTypes) - { - if ($topicType !~ / command$/ && - exists $topicTypePrefixes->{$topicType} && - !exists $usedTopicTypes{$topicType}) - { - print FH_LANGUAGES ' '; - - if ($topicTypePrefixes->{$topicType . ' command'}) - { print FH_LANGUAGES ucfirst($topicTypePrefixes->{$topicType . ' command'}) . ' Ignored '; } - else - { print FH_LANGUAGES 'Ignore '; }; - - if ($topicType ne ::TOPIC_GENERAL()) - { print FH_LANGUAGES NaturalDocs::Topics->TypeInfo($topicType)->Name() . ' '; }; - - my @prefixes = split(/ /, $topicTypePrefixes->{$topicType}, 2); - - if (scalar @prefixes == 1) - { print FH_LANGUAGES 'Prefix in Index: '; } - else - { print FH_LANGUAGES 'Prefixes in Index: '; }; - - print FH_LANGUAGES $topicTypePrefixes->{$topicType} . "\n"; - - $usedTopicTypes{$topicType} = 1; - }; - }; - }; - - if (exists $properties->{'line comments'}) - { - my @comments = split(/ /, $properties->{'line comments'}, 2); - - if (scalar @comments == 1) - { print FH_LANGUAGES ' Line Comment: '; } - else - { print FH_LANGUAGES ' Line Comments: '; }; - - print FH_LANGUAGES $properties->{'line comments'} . "\n"; - }; - - if (exists $properties->{'block comments'}) - { - my @comments = split(/ /, $properties->{'block comments'}, 3); - - if (scalar @comments == 2) - { print FH_LANGUAGES ' Block Comment: '; } - else - { print FH_LANGUAGES ' Block Comments: '; }; - - print FH_LANGUAGES $properties->{'block comments'} . "\n"; - }; - - if (exists $properties->{'package separator'}) - { - # Prior to 1.32, Package Separator was allowed for full language support. Ignore it when reformatting. - if ($version >= NaturalDocs::Version->FromString('1.32') || !exists $properties->{'full language support'}) - { print FH_LANGUAGES ' Package Separator: ' . $properties->{'package separator'} . "\n"; }; - }; - - if (exists $properties->{'enum values'}) - { - print FH_LANGUAGES ' Enum Values: ' . ucfirst(lc($properties->{'enum values'})) . "\n"; - }; - - if (exists $properties->{'prototype enders'}) - { - my $topicTypeEnders = $properties->{'prototype enders'}; - - my %usedTopicTypes; - my @topicTypes = ( @topicTypeOrder, keys %$topicTypeEnders ); - - foreach my $topicType (@topicTypes) - { - if ($topicType !~ / command$/ && - exists $topicTypeEnders->{$topicType} && - !exists $usedTopicTypes{$topicType}) - { - print FH_LANGUAGES ' '; - - if ($topicType ne ::TOPIC_GENERAL()) - { print FH_LANGUAGES NaturalDocs::Topics->TypeInfo($topicType)->Name() . ' '; }; - - my @enders = split(/ /, $topicTypeEnders->{$topicType}, 2); - - if (scalar @enders == 1) - { print FH_LANGUAGES 'Prototype Ender: '; } - else - { print FH_LANGUAGES 'Prototype Enders: '; }; - - print FH_LANGUAGES $topicTypeEnders->{$topicType} . "\n"; - - $usedTopicTypes{$topicType} = 1; - }; - }; - }; - - if (exists $properties->{'line extender'}) - { - print FH_LANGUAGES ' Line Extender: ' . $properties->{'line extender'} . "\n"; - }; - - if (exists $properties->{'perl package'}) - { - print FH_LANGUAGES ' Perl Package: ' . $properties->{'perl package'} . "\n"; - }; - - if (exists $properties->{'full language support'}) - { - print FH_LANGUAGES ' Full Language Support: ' . $properties->{'full language support'} . "\n"; - }; - }; - - close(FH_LANGUAGES); - }; - - - -############################################################################### -# Group: Functions - - -# -# Function: LanguageOf -# -# Returns the language of the passed source file. -# -# Parameters: -# -# sourceFile - The source <FileName> to get the language of. -# -# Returns: -# -# A <NaturalDocs::Languages::Base>-derived object for the passed file, or undef if the file is not a recognized language. -# -sub LanguageOf #(sourceFile) - { - my ($self, $sourceFile) = @_; - - my $extension = NaturalDocs::File->ExtensionOf($sourceFile); - if (defined $extension) - { $extension = lc($extension); }; - - my $languageName; - - if (!defined $extension) - { $languageName = 'shebang script'; } - else - { $languageName = $extensions{$extension}; }; - - if (!defined $languageName) - { $languageName = $extensions{'*'}; }; - - if (defined $languageName) - { - if ($languageName eq 'shebang script') - { - if (exists $shebangFiles{$sourceFile}) - { - if (defined $shebangFiles{$sourceFile}) - { return $languages{$shebangFiles{$sourceFile}}; } - else - { return undef; }; - } - - else # (!exists $shebangFiles{$sourceFile}) - { - my $shebangLine; - - open(SOURCEFILEHANDLE, '<' . $sourceFile) or die 'Could not open ' . $sourceFile; - - read(SOURCEFILEHANDLE, $shebangLine, 2); - if ($shebangLine eq '#!') - { $shebangLine = <SOURCEFILEHANDLE>; } - else - { $shebangLine = undef; }; - - close (SOURCEFILEHANDLE); - - if (!defined $shebangLine) - { - $shebangFiles{$sourceFile} = undef; - return undef; - } - else - { - $shebangLine = lc($shebangLine); - - foreach my $shebangString (keys %shebangStrings) - { - if (index($shebangLine, $shebangString) != -1) - { - $shebangFiles{$sourceFile} = $shebangStrings{$shebangString}; - return $languages{$shebangStrings{$shebangString}}; - }; - }; - - $shebangFiles{$sourceFile} = undef; - return undef; - }; - }; - } - - else # language name ne 'shebang script' - { return $languages{$languageName}; }; - } - else # !defined $language - { - return undef; - }; - }; - - -# -# Function: OnMostUsedLanguageKnown -# -# Called when the most used language is known. -# -sub OnMostUsedLanguageKnown - { - my $self = shift; - - my $language = $languages{lc( NaturalDocs::Project->MostUsedLanguage() )}; - - if ($language) - { - if (!$languages{'text file'}->HasIgnoredPrefixes()) - { $languages{'text file'}->CopyIgnoredPrefixesOf($language); }; - if (!$languages{'text file'}->PackageSeparatorWasSet()) - { $languages{'text file'}->SetPackageSeparator($language->PackageSeparator()); }; - if (!$languages{'text file'}->EnumValuesWasSet()) - { $languages{'text file'}->SetEnumValues($language->EnumValues()); }; - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/ActionScript.pm b/docs/doctool/Modules/NaturalDocs/Languages/ActionScript.pm deleted file mode 100644 index 33f3b73d..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/ActionScript.pm +++ /dev/null @@ -1,885 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::ActionScript -# -############################################################################### -# -# A subclass to handle the language variations of Flash ActionScript. -# -# -# Topic: Language Support -# -# Supported: -# -# Not supported yet: -# -############################################################################### - -# 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::Languages::ActionScript; - -use base 'NaturalDocs::Languages::Advanced'; - - -################################################################################ -# Group: Package Variables - -# -# hash: classModifiers -# An existence hash of all the acceptable class modifiers. The keys are in all lowercase. -# -my %classModifiers = ( 'dynamic' => 1, - 'intrinsic' => 1 ); - -# -# hash: memberModifiers -# An existence hash of all the acceptable class member modifiers. The keys are in all lowercase. -# -my %memberModifiers = ( 'public' => 1, - 'private' => 1, - 'static' => 1 ); - - -# -# hash: declarationEnders -# An existence hash of all the tokens that can end a declaration. This is important because statements don't require a semicolon -# to end. The keys are in all lowercase. -# -my %declarationEnders = ( ';' => 1, - '}' => 1, - '{' => 1, - 'public' => 1, - 'private' => 1, - 'static' => 1, - 'class' => 1, - 'interface' => 1, - 'var' => 1, - 'function' => 1, - 'import' => 1 ); - - - -################################################################################ -# Group: Interface Functions - - -# -# Function: PackageSeparator -# Returns the package separator symbol. -# -sub PackageSeparator - { return '.'; }; - - -# -# Function: EnumValues -# Returns the <EnumValuesType> that describes how the language handles enums. -# -sub EnumValues - { return ::ENUM_GLOBAL(); }; - - -# -# Function: ParseParameterLine -# Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object. -# -sub ParseParameterLine #(line) - { - my ($self, $line) = @_; - return $self->ParsePascalParameterLine($line); - }; - - -# -# Function: TypeBeforeParameter -# Returns whether the type appears before the parameter in prototypes. -# -sub TypeBeforeParameter - { return 0; }; - - -# -# Function: ParseFile -# -# Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>. -# -# Parameters: -# -# sourceFile - The <FileName> to parse. -# topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file. -# -# Returns: -# -# The array ( autoTopics, scopeRecord ). -# -# autoTopics - An arrayref of automatically generated topics from the file, or undef if none. -# scopeRecord - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none. -# -sub ParseFile #(sourceFile, topicsList) - { - my ($self, $sourceFile, $topicsList) = @_; - - $self->ParseForCommentsAndTokens($sourceFile, [ '//' ], [ '/*', '*/' ] ); - - my $tokens = $self->Tokens(); - my $index = 0; - my $lineNumber = 1; - - while ($index < scalar @$tokens) - { - if ($self->TryToSkipWhitespace(\$index, \$lineNumber) || - $self->TryToGetImport(\$index, \$lineNumber) || - $self->TryToGetClass(\$index, \$lineNumber) || - $self->TryToGetFunction(\$index, \$lineNumber) || - $self->TryToGetVariable(\$index, \$lineNumber) ) - { - # The functions above will handle everything. - } - - elsif ($tokens->[$index] eq '{') - { - $self->StartScope('}', $lineNumber, undef, undef, undef); - $index++; - } - - elsif ($tokens->[$index] eq '}') - { - if ($self->ClosingScopeSymbol() eq '}') - { $self->EndScope($lineNumber); }; - - $index++; - } - - else - { - $self->SkipToNextStatement(\$index, \$lineNumber); - }; - }; - - - # Don't need to keep these around. - $self->ClearTokens(); - - - my $autoTopics = $self->AutoTopics(); - - my $scopeRecord = $self->ScopeRecord(); - if (defined $scopeRecord && !scalar @$scopeRecord) - { $scopeRecord = undef; }; - - return ( $autoTopics, $scopeRecord ); - }; - - - -################################################################################ -# Group: Statement Parsing Functions -# All functions here assume that the current position is at the beginning of a statement. -# -# Note for developers: I am well aware that the code in these functions do not check if we're past the end of the tokens as -# often as it should. We're making use of the fact that Perl will always return undef in these cases to keep the code simpler. - - -# -# Function: TryToGetIdentifier -# -# Determines whether the position is at an identifier, and if so, skips it and returns the complete identifier as a string. Returns -# undef otherwise. -# -# Parameters: -# -# indexRef - A reference to the current token index. -# lineNumberRef - A reference to the current line number. -# allowStar - If set, allows the last identifier to be a star. -# -sub TryToGetIdentifier #(indexRef, lineNumberRef, allowStar) - { - my ($self, $indexRef, $lineNumberRef, $allowStar) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - - use constant MODE_IDENTIFIER_START => 1; - use constant MODE_IN_IDENTIFIER => 2; - use constant MODE_AFTER_STAR => 3; - - my $identifier; - my $mode = MODE_IDENTIFIER_START; - - while ($index < scalar @$tokens) - { - if ($mode == MODE_IDENTIFIER_START) - { - if ($tokens->[$index] =~ /^[a-z\$\_]/i) - { - $identifier .= $tokens->[$index]; - $index++; - - $mode = MODE_IN_IDENTIFIER; - } - elsif ($allowStar && $tokens->[$index] eq '*') - { - $identifier .= '*'; - $index++; - - $mode = MODE_AFTER_STAR; - } - else - { return undef; }; - } - - elsif ($mode == MODE_IN_IDENTIFIER) - { - if ($tokens->[$index] eq '.') - { - $identifier .= '.'; - $index++; - - $mode = MODE_IDENTIFIER_START; - } - elsif ($tokens->[$index] =~ /^[a-z0-9\$\_]/i) - { - $identifier .= $tokens->[$index]; - $index++; - } - else - { last; }; - } - - else #($mode == MODE_AFTER_STAR) - { - if ($tokens->[$index] =~ /^[a-z0-9\$\_\.]/i) - { return undef; } - else - { last; }; - }; - }; - - # We need to check again because we may have run out of tokens after a dot. - if ($mode != MODE_IDENTIFIER_START) - { - $$indexRef = $index; - return $identifier; - } - else - { return undef; }; - }; - - -# -# Function: TryToGetImport -# -# Determines whether the position is at a import statement, and if so, adds it as a Using statement to the current scope, skips -# it, and returns true. -# -sub TryToGetImport #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - if ($tokens->[$index] ne 'import') - { return undef; }; - - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my $identifier = $self->TryToGetIdentifier(\$index, \$lineNumber, 1); - if (!$identifier) - { return undef; }; - - - # Currently we implement importing by stripping the last package level and treating it as a using. So "import p1.p2.p3" makes - # p1.p2 the using path, which is over-tolerant but that's okay. "import p1.p2.*" is treated the same way, but in this case it's - # not over-tolerant. If there's no dot, there's no point to including it. - - if (index($identifier, '.') != -1) - { - $identifier =~ s/\.[^\.]+$//; - $self->AddUsing( NaturalDocs::SymbolString->FromText($identifier) ); - }; - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - -# -# Function: TryToGetClass -# -# Determines whether the position is at a class declaration statement, and if so, generates a topic for it, skips it, and -# returns true. -# -# Supported Syntaxes: -# -# - Classes -# - Interfaces -# - Classes and interfaces with _global -# -sub TryToGetClass #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - my @modifiers; - - while ($tokens->[$index] =~ /^[a-z]/i && - exists $classModifiers{lc($tokens->[$index])} ) - { - push @modifiers, lc($tokens->[$index]); - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - my $type; - - if ($tokens->[$index] eq 'class' || $tokens->[$index] eq 'interface') - { - $type = $tokens->[$index]; - - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - } - else - { return undef; }; - - my $className = $self->TryToGetIdentifier(\$index, \$lineNumber); - - if (!$className) - { return undef; }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my @parents; - - if ($tokens->[$index] eq 'extends') - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my $parent = $self->TryToGetIdentifier(\$index, \$lineNumber); - if (!$parent) - { return undef; }; - - push @parents, $parent; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - if ($type eq 'class' && $tokens->[$index] eq 'implements') - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - for (;;) - { - my $parent = $self->TryToGetIdentifier(\$index, \$lineNumber); - if (!$parent) - { return undef; }; - - push @parents, $parent; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if ($tokens->[$index] ne ',') - { last; } - else - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - }; - }; - - if ($tokens->[$index] ne '{') - { return undef; }; - - $index++; - - - # If we made it this far, we have a valid class declaration. - - my $topicType; - - if ($type eq 'interface') - { $topicType = ::TOPIC_INTERFACE(); } - else - { $topicType = ::TOPIC_CLASS(); }; - - $className =~ s/^_global.//; - - my $autoTopic = NaturalDocs::Parser::ParsedTopic->New($topicType, $className, - undef, $self->CurrentUsing(), - undef, - undef, undef, $$lineNumberRef); - - $self->AddAutoTopic($autoTopic); - NaturalDocs::Parser->OnClass($autoTopic->Package()); - - foreach my $parent (@parents) - { - NaturalDocs::Parser->OnClassParent($autoTopic->Package(), NaturalDocs::SymbolString->FromText($parent), - undef, $self->CurrentUsing(), ::RESOLVE_ABSOLUTE()); - }; - - $self->StartScope('}', $lineNumber, $autoTopic->Package()); - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - -# -# Function: TryToGetFunction -# -# Determines if the position is on a function declaration, and if so, generates a topic for it, skips it, and returns true. -# -# Supported Syntaxes: -# -# - Functions -# - Constructors -# - Properties -# - Functions with _global -# -sub TryToGetFunction #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - my $startIndex = $index; - my $startLine = $lineNumber; - - my @modifiers; - - while ($tokens->[$index] =~ /^[a-z]/i && - exists $memberModifiers{lc($tokens->[$index])} ) - { - push @modifiers, lc($tokens->[$index]); - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - if ($tokens->[$index] ne 'function') - { return undef; }; - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my $type; - - if ($tokens->[$index] eq 'get' || $tokens->[$index] eq 'set') - { - # This can either be a property ("function get Something()") or a function name ("function get()"). - - my $nextIndex = $index; - my $nextLineNumber = $lineNumber; - - $nextIndex++; - $self->TryToSkipWhitespace(\$nextIndex, \$nextLineNumber); - - if ($tokens->[$nextIndex] eq '(') - { - $type = ::TOPIC_FUNCTION(); - # Ignore the movement and let the code ahead pick it up as the name. - } - else - { - $type = ::TOPIC_PROPERTY(); - $index = $nextIndex; - $lineNumber = $nextLineNumber; - }; - } - else - { $type = ::TOPIC_FUNCTION(); }; - - my $name = $self->TryToGetIdentifier(\$index, \$lineNumber); - if (!$name) - { return undef; }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if ($tokens->[$index] ne '(') - { return undef; }; - - $index++; - $self->GenericSkipUntilAfter(\$index, \$lineNumber, ')'); - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if ($tokens->[$index] eq ':') - { - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - $self->TryToGetIdentifier(\$index, \$lineNumber); - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - - my $prototype = $self->NormalizePrototype( $self->CreateString($startIndex, $index) ); - - if ($tokens->[$index] eq '{') - { $self->GenericSkip(\$index, \$lineNumber); } - elsif (!exists $declarationEnders{$tokens->[$index]}) - { return undef; }; - - - my $scope = $self->CurrentScope(); - - if ($name =~ s/^_global.//) - { $scope = undef; }; - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($type, $name, - $scope, $self->CurrentUsing(), - $prototype, - undef, undef, $startLine)); - - - # We succeeded if we got this far. - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - -# -# Function: TryToGetVariable -# -# Determines if the position is on a variable declaration statement, and if so, generates a topic for each variable, skips the -# statement, and returns true. -# -# Supported Syntaxes: -# -# - Variables -# - Variables with _global -# -sub TryToGetVariable #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - my $startIndex = $index; - my $startLine = $lineNumber; - - my @modifiers; - - while ($tokens->[$index] =~ /^[a-z]/i && - exists $memberModifiers{lc($tokens->[$index])} ) - { - push @modifiers, lc($tokens->[$index]); - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - if ($tokens->[$index] ne 'var') - { return undef; }; - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my $endTypeIndex = $index; - my @names; - my @types; - - for (;;) - { - my $name = $self->TryToGetIdentifier(\$index, \$lineNumber); - if (!$name) - { return undef; }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my $type; - - if ($tokens->[$index] eq ':') - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - $type = ': ' . $self->TryToGetIdentifier(\$index, \$lineNumber); - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - if ($tokens->[$index] eq '=') - { - do - { - $self->GenericSkip(\$index, \$lineNumber); - } - while ($tokens->[$index] ne ',' && !exists $declarationEnders{$tokens->[$index]} && $index < scalar @$tokens); - }; - - push @names, $name; - push @types, $type; - - if ($tokens->[$index] eq ',') - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - } - elsif (exists $declarationEnders{$tokens->[$index]}) - { last; } - else - { return undef; }; - }; - - - # We succeeded if we got this far. - - my $prototypePrefix = $self->CreateString($startIndex, $endTypeIndex); - - for (my $i = 0; $i < scalar @names; $i++) - { - my $prototype = $self->NormalizePrototype( $prototypePrefix . ' ' . $names[$i] . $types[$i]); - my $scope = $self->CurrentScope(); - - if ($names[$i] =~ s/^_global.//) - { $scope = undef; }; - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_VARIABLE(), $names[$i], - $scope, $self->CurrentUsing(), - $prototype, - undef, undef, $startLine)); - }; - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - - -################################################################################ -# Group: Low Level Parsing Functions - - -# -# Function: GenericSkip -# -# Advances the position one place through general code. -# -# - If the position is on a string, it will skip it completely. -# - If the position is on an opening symbol, it will skip until the past the closing symbol. -# - If the position is on whitespace (including comments), it will skip it completely. -# - Otherwise it skips one token. -# -# Parameters: -# -# indexRef - A reference to the current index. -# lineNumberRef - A reference to the current line number. -# -sub GenericSkip #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - # We can ignore the scope stack because we're just skipping everything without parsing, and we need recursion anyway. - if ($tokens->[$$indexRef] eq '{') - { - $$indexRef++; - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}'); - } - elsif ($tokens->[$$indexRef] eq '(') - { - $$indexRef++; - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ')'); - } - elsif ($tokens->[$$indexRef] eq '[') - { - $$indexRef++; - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ']'); - } - - elsif ($self->TryToSkipWhitespace($indexRef, $lineNumberRef) || - $self->TryToSkipString($indexRef, $lineNumberRef)) - { - } - - else - { $$indexRef++; }; - }; - - -# -# Function: GenericSkipUntilAfter -# -# Advances the position via <GenericSkip()> until a specific token is reached and passed. -# -sub GenericSkipUntilAfter #(indexRef, lineNumberRef, token) - { - my ($self, $indexRef, $lineNumberRef, $token) = @_; - my $tokens = $self->Tokens(); - - while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne $token) - { $self->GenericSkip($indexRef, $lineNumberRef); }; - - if ($tokens->[$$indexRef] eq "\n") - { $$lineNumberRef++; }; - $$indexRef++; - }; - - -# -# Function: SkipToNextStatement -# -# Advances the position via <GenericSkip()> until the next statement, which is defined as anything in <declarationEnders> not -# appearing in brackets or strings. It will always advance at least one token. -# -sub SkipToNextStatement #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - do - { - $self->GenericSkip($indexRef, $lineNumberRef); - } - while ( $$indexRef < scalar @$tokens && - !exists $declarationEnders{$tokens->[$$indexRef]} ); - }; - - -# -# Function: TryToSkipString -# If the current position is on a string delimiter, skip past the string and return true. -# -# Parameters: -# -# indexRef - A reference to the index of the position to start at. -# lineNumberRef - A reference to the line number of the position. -# -# Returns: -# -# Whether the position was at a string. -# -# Syntax Support: -# -# - Supports quotes, apostrophes, and at-quotes. -# -sub TryToSkipString #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - - return ($self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '\'') || - $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '"') ); - }; - - -# -# Function: TryToSkipWhitespace -# If the current position is on a whitespace token, a line break token, or a comment, it skips them and returns true. If there are -# a number of these in a row, it skips them all. -# -sub TryToSkipWhitespace #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $result; - - while ($$indexRef < scalar @$tokens) - { - if ($tokens->[$$indexRef] =~ /^[ \t]/) - { - $$indexRef++; - $result = 1; - } - elsif ($tokens->[$$indexRef] eq "\n") - { - $$indexRef++; - $$lineNumberRef++; - $result = 1; - } - elsif ($self->TryToSkipComment($indexRef, $lineNumberRef)) - { - $result = 1; - } - else - { last; }; - }; - - return $result; - }; - - -# -# Function: TryToSkipComment -# If the current position is on a comment, skip past it and return true. -# -sub TryToSkipComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - - return ( $self->TryToSkipLineComment($indexRef, $lineNumberRef) || - $self->TryToSkipMultilineComment($indexRef, $lineNumberRef) ); - }; - - -# -# Function: TryToSkipLineComment -# If the current position is on a line comment symbol, skip past it and return true. -# -sub TryToSkipLineComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - if ($tokens->[$$indexRef] eq '/' && $tokens->[$$indexRef+1] eq '/') - { - $self->SkipRestOfLine($indexRef, $lineNumberRef); - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToSkipMultilineComment -# If the current position is on an opening comment symbol, skip past it and return true. -# -sub TryToSkipMultilineComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - if ($tokens->[$$indexRef] eq '/' && $tokens->[$$indexRef+1] eq '*') - { - $self->SkipUntilAfter($indexRef, $lineNumberRef, '*', '/'); - return 1; - } - else - { return undef; }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Ada.pm b/docs/doctool/Modules/NaturalDocs/Languages/Ada.pm deleted file mode 100644 index b2467799..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Ada.pm +++ /dev/null @@ -1,38 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Ada -# -############################################################################### -# -# A subclass to handle the language variations of Ada -# -############################################################################### - -# 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::Languages::Ada; - -use base 'NaturalDocs::Languages::Simple'; - - -# -# Function: ParseParameterLine -# Overridden because Ada uses Pascal-style parameters -# -sub ParseParameterLine #(...) - { - my ($self, @params) = @_; - return $self->SUPER::ParsePascalParameterLine(@params); - }; - -sub TypeBeforeParameter - { - return 0; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Advanced.pm b/docs/doctool/Modules/NaturalDocs/Languages/Advanced.pm deleted file mode 100644 index 98ea8884..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Advanced.pm +++ /dev/null @@ -1,801 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Advanced -# -############################################################################### -# -# The base class for all languages that have full support in Natural Docs. Each one will have a custom parser capable -# of documenting undocumented aspects of the code. -# -############################################################################### - -# 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; - -use NaturalDocs::Languages::Advanced::Scope; -use NaturalDocs::Languages::Advanced::ScopeChange; - -package NaturalDocs::Languages::Advanced; - -use base 'NaturalDocs::Languages::Base'; - - -############################################################################# -# Group: Implementation - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The following constants are used as indexes. -# -# TOKENS - An arrayref of tokens used in all the <Parsing Functions>. -# SCOPE_STACK - An arrayref of <NaturalDocs::Languages::Advanced::Scope> objects serving as a scope stack for parsing. -# There will always be one available, with a symbol of undef, for the top level. -# SCOPE_RECORD - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChange> objects, as generated by the scope -# stack. If there is more than one change per line, only the last is stored. -# AUTO_TOPICS - An arrayref of <NaturalDocs::Parser::ParsedTopics> generated automatically from the code. -# -use NaturalDocs::DefineMembers 'TOKENS', 'SCOPE_STACK', 'SCOPE_RECORD', 'AUTO_TOPICS'; - - -############################################################################# -# Group: Functions - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# name - The name of the language. -# -sub New #(name) - { - my ($package, @parameters) = @_; - - my $object = $package->SUPER::New(@parameters); - $object->[TOKENS] = undef; - $object->[SCOPE_STACK] = undef; - $object->[SCOPE_RECORD] = undef; - - return $object; - }; - - -# Function: Tokens -# Returns the tokens found by <ParseForCommentsAndTokens()>. -sub Tokens - { return $_[0]->[TOKENS]; }; - -# Function: SetTokens -# Replaces the tokens. -sub SetTokens #(tokens) - { $_[0]->[TOKENS] = $_[1]; }; - -# Function: ClearTokens -# Resets the token list. You may want to do this after parsing is over to save memory. -sub ClearTokens - { $_[0]->[TOKENS] = undef; }; - -# Function: AutoTopics -# Returns the arrayref of automatically generated topics, or undef if none. -sub AutoTopics - { return $_[0]->[AUTO_TOPICS]; }; - -# Function: AddAutoTopic -# Adds a <NaturalDocs::Parser::ParsedTopic> to <AutoTopics()>. -sub AddAutoTopic #(topic) - { - my ($self, $topic) = @_; - if (!defined $self->[AUTO_TOPICS]) - { $self->[AUTO_TOPICS] = [ ]; }; - push @{$self->[AUTO_TOPICS]}, $topic; - }; - -# Function: ClearAutoTopics -# Resets the automatic topic list. Not necessary if you call <ParseForCommentsAndTokens()>. -sub ClearAutoTopics - { $_[0]->[AUTO_TOPICS] = undef; }; - -# Function: ScopeRecord -# Returns an arrayref of <NaturalDocs::Languages::Advanced::ScopeChange> objects describing how and when the scope -# changed thoughout the file. There will always be at least one entry, which will be for line 1 and undef as the scope. -sub ScopeRecord - { return $_[0]->[SCOPE_RECORD]; }; - - - -############################################################################### -# -# Group: Parsing Functions -# -# These functions are good general language building blocks. Use them to create your language-specific parser. -# -# All functions work on <Tokens()> and assume it is set by <ParseForCommentsAndTokens()>. -# - - -# -# Function: ParseForCommentsAndTokens -# -# Loads the passed file, sends all appropriate comments to <NaturalDocs::Parser->OnComment()>, and breaks the rest into -# an arrayref of tokens. Tokens are defined as -# -# - All consecutive alphanumeric and underscore characters. -# - All consecutive whitespace. -# - A single line break. It will always be "\n"; you don't have to worry about platform differences. -# - A single character not included above, which is usually a symbol. Multiple consecutive ones each get their own token. -# -# The result will be placed in <Tokens()>. -# -# Parameters: -# -# sourceFile - The source <FileName> to load and parse. -# lineCommentSymbols - An arrayref of symbols that designate line comments, or undef if none. -# blockCommentSymbols - An arrayref of symbol pairs that designate multiline comments, or undef if none. Symbol pairs are -# designated as two consecutive array entries, the opening symbol appearing first. -# -# Notes: -# -# - This function automatically calls <ClearAutoTopics()> and <ClearScopeStack()>. You only need to call those functions -# manually if you override this one. -# - To save parsing time, all comment lines sent to <NaturalDocs::Parser->OnComment()> will be replaced with blank lines -# in <Tokens()>. It's all the same to most languages. -# -sub ParseForCommentsAndTokens #(sourceFile, lineCommentSymbols, blockCommentSymbols) - { - my ($self, $sourceFile, $lineCommentSymbols, $blockCommentSymbols) = @_; - - open(SOURCEFILEHANDLE, '<' . $sourceFile) - or die "Couldn't open input file " . $sourceFile . "\n"; - - my $tokens = [ ]; - $self->SetTokens($tokens); - - # For convenience. - $self->ClearAutoTopics(); - $self->ClearScopeStack(); - - my @commentLines; - - my $line = <SOURCEFILEHANDLE>; - my $lineNumber = 1; - - # On the very first line, remove a Unicode BOM if present. Information on it available at: - # http://www.unicode.org/faq/utf_bom.html#BOM - $line =~ s/^\xEF\xBB\xBF//; - - while (defined $line) - { - ::XChomp(\$line); - $self->PreprocessLine(\$line); - - my $originalLine = $line; - my $closingSymbol; - - - # Retrieve single line comments. This leaves $line at the next line. - - if ($self->StripOpeningSymbols(\$line, $lineCommentSymbols)) - { - do - { - push @commentLines, $line; - push @$tokens, "\n"; - $line = <SOURCEFILEHANDLE>; - - if (!defined $line) - { goto EndDo; }; - - ::XChomp(\$line); - $self->PreprocessLine(\$line); - } - while ($self->StripOpeningSymbols(\$line, $lineCommentSymbols)); - - EndDo: # I hate Perl sometimes. - } - - - # Retrieve multiline comments. This leaves $line at the next line. - - elsif ($closingSymbol = $self->StripOpeningBlockSymbols(\$line, $blockCommentSymbols)) - { - # Note that it is possible for a multiline comment to start correctly but not end so. We want those comments to stay in - # the code. For example, look at this prototype with this splint annotation: - # - # int get_array(integer_t id, - # /*@out@*/ array_t array); - # - # The annotation starts correctly but doesn't end so because it is followed by code on the same line. - - my ($symbol, $lineRemainder, $isMultiLine); - - for (;;) - { - ($symbol, $lineRemainder) = $self->StripClosingSymbol(\$line, $closingSymbol); - - push @commentLines, $line; - - # If we found an end comment symbol... - if (defined $symbol) - { last; }; - - push @$tokens, "\n"; - $line = <SOURCEFILEHANDLE>; - $isMultiLine = 1; - - if (!defined $line) - { last; }; - - ::XChomp(\$line); - $self->PreprocessLine(\$line); - }; - - if ($lineRemainder !~ /^[ \t]*$/) - { - # If there was something past the closing symbol this wasn't an acceptable comment. - - if ($isMultiLine) - { $self->TokenizeLine($lineRemainder); } - else - { - # We go back to the original line if it wasn't a multiline comment because we want the comment to stay in the - # code. Otherwise the /*@out@*/ from the example would be removed. - $self->TokenizeLine($originalLine); - }; - - $lineNumber += scalar @commentLines; - @commentLines = ( ); - } - else - { - push @$tokens, "\n"; - }; - - $line = <SOURCEFILEHANDLE>; - } - - - # Otherwise just add it to the code. - - else - { - $self->TokenizeLine($line); - $lineNumber++; - $line = <SOURCEFILEHANDLE>; - }; - - - # If there were comments, send them to Parser->OnComment(). - - if (scalar @commentLines) - { - NaturalDocs::Parser->OnComment(\@commentLines, $lineNumber); - $lineNumber += scalar @commentLines; - @commentLines = ( ); - }; - - }; # while (defined $line) - - - close(SOURCEFILEHANDLE); - } - - -# -# Function: PreprocessLine -# -# An overridable function if you'd like to preprocess a text line before it goes into <ParseForCommentsAndTokens()>. -# -# Parameters: -# -# lineRef - A reference to the line. Already has the line break stripped off, but is otherwise untouched. -# -sub PreprocessLine #(lineRef) - { - }; - - -# -# Function: TokenizeLine -# -# Converts the passed line to tokens as described in <ParseForCommentsAndTokens> and adds them to <Tokens()>. Also -# adds a line break token after it. -# -sub TokenizeLine #(line) - { - my ($self, $line) = @_; - push @{$self->Tokens()}, $line =~ /(\w+|[ \t]+|.)/g, "\n"; - }; - - -# -# Function: TryToSkipString -# -# If the position is on a string delimiter, moves the position to the token following the closing delimiter, or past the end of the -# tokens if there is none. Assumes all other characters are allowed in the string, the delimiter itself is allowed if it's preceded by -# a backslash, and line breaks are allowed in the string. -# -# Parameters: -# -# indexRef - A reference to the position's index into <Tokens()>. -# lineNumberRef - A reference to the position's line number. -# openingDelimiter - The opening string delimiter, such as a quote or an apostrophe. -# closingDelimiter - The closing string delimiter, if different. If not defined, assumes the same as openingDelimiter. -# startContentIndexRef - A reference to a variable in which to store the index of the first token of the string's content. -# May be undef. -# endContentIndexRef - A reference to a variable in which to store the index of the end of the string's content, which is one -# past the last index of content. May be undef. -# -# Returns: -# -# Whether the position was on the passed delimiter or not. The index, line number, and content index ref variables will be -# updated only if true. -# -sub TryToSkipString #(indexRef, lineNumberRef, openingDelimiter, closingDelimiter, startContentIndexRef, endContentIndexRef) - { - my ($self, $index, $lineNumber, $openingDelimiter, $closingDelimiter, $startContentIndexRef, $endContentIndexRef) = @_; - my $tokens = $self->Tokens(); - - if (!defined $closingDelimiter) - { $closingDelimiter = $openingDelimiter; }; - - if ($tokens->[$$index] ne $openingDelimiter) - { return undef; }; - - - $$index++; - if (defined $startContentIndexRef) - { $$startContentIndexRef = $$index; }; - - while ($$index < scalar @$tokens) - { - if ($tokens->[$$index] eq "\\") - { - # Skip the token after it. - $$index += 2; - } - elsif ($tokens->[$$index] eq "\n") - { - $$lineNumber++; - $$index++; - } - elsif ($tokens->[$$index] eq $closingDelimiter) - { - if (defined $endContentIndexRef) - { $$endContentIndexRef = $$index; }; - - $$index++; - last; - } - else - { - $$index++; - }; - }; - - if ($$index >= scalar @$tokens && defined $endContentIndexRef) - { $$endContentIndexRef = scalar @$tokens; }; - - return 1; - }; - - -# -# Function: SkipRestOfLine -# -# Moves the position to the token following the next line break, or past the end of the tokens array if there is none. Useful for -# line comments. -# -# Note that it skips blindly. It assumes there cannot be anything of interest, such as a string delimiter, between the position -# and the end of the line. -# -# Parameters: -# -# indexRef - A reference to the position's index into <Tokens()>. -# lineNumberRef - A reference to the position's line number. - -sub SkipRestOfLine #(indexRef, lineNumberRef) - { - my ($self, $index, $lineNumber) = @_; - my $tokens = $self->Tokens(); - - while ($$index < scalar @$tokens) - { - if ($tokens->[$$index] eq "\n") - { - $$lineNumber++; - $$index++; - last; - } - else - { - $$index++; - }; - }; - }; - - -# -# Function: SkipUntilAfter -# -# Moves the position to the token following the next occurance of a particular token sequence, or past the end of the tokens -# array if it never occurs. Useful for multiline comments. -# -# Note that it skips blindly. It assumes there cannot be anything of interest, such as a string delimiter, between the position -# and the end of the line. -# -# Parameters: -# -# indexRef - A reference to the position's index. -# lineNumberRef - A reference to the position's line number. -# token - A token that must be matched. Can be specified multiple times to match a sequence of tokens. -# -sub SkipUntilAfter #(indexRef, lineNumberRef, token, token, ...) - { - my ($self, $index, $lineNumber, @target) = @_; - my $tokens = $self->Tokens(); - - while ($$index < scalar @$tokens) - { - if ($tokens->[$$index] eq $target[0] && ($$index + scalar @target) <= scalar @$tokens) - { - my $match = 1; - - for (my $i = 1; $i < scalar @target; $i++) - { - if ($tokens->[$$index+$i] ne $target[$i]) - { - $match = 0; - last; - }; - }; - - if ($match) - { - $$index += scalar @target; - return; - }; - }; - - if ($tokens->[$index] eq "\n") - { - $$lineNumber++; - $$index++; - } - else - { - $$index++; - }; - }; - }; - - -# -# Function: IsFirstLineToken -# -# Returns whether the position is at the first token of a line, not including whitespace. -# -# Parameters: -# -# index - The index of the position. -# -sub IsFirstLineToken #(index) - { - my ($self, $index) = @_; - my $tokens = $self->Tokens(); - - if ($index == 0) - { return 1; }; - - $index--; - - if ($tokens->[$index] =~ /^[ \t]/) - { $index--; }; - - if ($index <= 0 || $tokens->[$index] eq "\n") - { return 1; } - else - { return undef; }; - }; - - -# -# Function: IsLastLineToken -# -# Returns whether the position is at the last token of a line, not including whitespace. -# -# Parameters: -# -# index - The index of the position. -# -sub IsLastLineToken #(index) - { - my ($self, $index) = @_; - my $tokens = $self->Tokens(); - - do - { $index++; } - while ($index < scalar @$tokens && $tokens->[$index] =~ /^[ \t]/); - - if ($index >= scalar @$tokens || $tokens->[$index] eq "\n") - { return 1; } - else - { return undef; }; - }; - - -# -# Function: IsAtSequence -# -# Returns whether the position is at a sequence of tokens. -# -# Parameters: -# -# index - The index of the position. -# token - A token to match. Specify multiple times to specify the sequence. -# -sub IsAtSequence #(index, token, token, token ...) - { - my ($self, $index, @target) = @_; - my $tokens = $self->Tokens(); - - if ($index + scalar @target > scalar @$tokens) - { return undef; }; - - for (my $i = 0; $i < scalar @target; $i++) - { - if ($tokens->[$i] ne $target[$i]) - { return undef; }; - }; - - return 1; - }; - - -# -# Function: IsBackslashed -# -# Returns whether the position is after a backslash. -# -# Parameters: -# -# index - The index of the postition. -# -sub IsBackslashed #(index) - { - my ($self, $index) = @_; - my $tokens = $self->Tokens(); - - if ($index > 0 && $tokens->[$index - 1] eq "\\") - { return 1; } - else - { return undef; }; - }; - - - -############################################################################### -# -# Group: Scope Functions -# -# These functions provide a nice scope stack implementation for language-specific parsers to use. The default implementation -# makes the following assumptions. -# -# - Packages completely replace one another, rather than concatenating. You need to concatenate manually if that's the -# behavior. -# -# - Packages inherit, so if a scope level doesn't set its own, the package is the same as the parent scope's. -# - - -# -# Function: ClearScopeStack -# -# Clears the scope stack for a new file. Not necessary if you call <ParseForCommentsAndTokens()>. -# -sub ClearScopeStack - { - my ($self) = @_; - $self->[SCOPE_STACK] = [ NaturalDocs::Languages::Advanced::Scope->New(undef, undef) ]; - $self->[SCOPE_RECORD] = [ NaturalDocs::Languages::Advanced::ScopeChange->New(undef, 1) ]; - }; - - -# -# Function: StartScope -# -# Records a new scope level. -# -# Parameters: -# -# closingSymbol - The closing symbol of the scope. -# lineNumber - The line number where the scope begins. -# package - The package <SymbolString> of the scope. Undef means no change. -# -sub StartScope #(closingSymbol, lineNumber, package) - { - my ($self, $closingSymbol, $lineNumber, $package) = @_; - - push @{$self->[SCOPE_STACK]}, - NaturalDocs::Languages::Advanced::Scope->New($closingSymbol, $package, $self->CurrentUsing()); - - $self->AddToScopeRecord($self->CurrentScope(), $lineNumber); - }; - - -# -# Function: EndScope -# -# Records the end of the current scope level. Note that this is blind; you need to manually check <ClosingScopeSymbol()> if -# you need to determine if it is correct to do so. -# -# Parameters: -# -# lineNumber - The line number where the scope ends. -# -sub EndScope #(lineNumber) - { - my ($self, $lineNumber) = @_; - - if (scalar @{$self->[SCOPE_STACK]} > 1) - { pop @{$self->[SCOPE_STACK]}; }; - - $self->AddToScopeRecord($self->CurrentScope(), $lineNumber); - }; - - -# -# Function: ClosingScopeSymbol -# -# Returns the symbol that ends the current scope level, or undef if we are at the top level. -# -sub ClosingScopeSymbol - { - my ($self) = @_; - return $self->[SCOPE_STACK]->[-1]->ClosingSymbol(); - }; - - -# -# Function: CurrentScope -# -# Returns the current calculated scope, or undef if global. The default implementation just returns <CurrentPackage()>. This -# is a separate function because C++ may need to track namespaces and classes separately, and so the current scope would -# be a concatenation of them. -# -sub CurrentScope - { - return $_[0]->CurrentPackage(); - }; - - -# -# Function: CurrentPackage -# -# Returns the current calculated package or class, or undef if none. -# -sub CurrentPackage - { - my ($self) = @_; - - my $package; - - for (my $index = scalar @{$self->[SCOPE_STACK]} - 1; $index >= 0 && !defined $package; $index--) - { - $package = $self->[SCOPE_STACK]->[$index]->Package(); - }; - - return $package; - }; - - -# -# Function: SetPackage -# -# Sets the package for the current scope level. -# -# Parameters: -# -# package - The new package <SymbolString>. -# lineNumber - The line number the new package starts on. -# -sub SetPackage #(package, lineNumber) - { - my ($self, $package, $lineNumber) = @_; - $self->[SCOPE_STACK]->[-1]->SetPackage($package); - - $self->AddToScopeRecord($self->CurrentScope(), $lineNumber); - }; - - -# -# Function: CurrentUsing -# -# Returns the current calculated arrayref of <SymbolStrings> from Using statements, or undef if none. -# -sub CurrentUsing - { - my ($self) = @_; - return $self->[SCOPE_STACK]->[-1]->Using(); - }; - - -# -# Function: AddUsing -# -# Adds a Using <SymbolString> to the current scope. -# -sub AddUsing #(using) - { - my ($self, $using) = @_; - $self->[SCOPE_STACK]->[-1]->AddUsing($using); - }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: AddToScopeRecord -# -# Adds a change to the scope record, condensing unnecessary entries. -# -# Parameters: -# -# newScope - What the scope <SymbolString> changed to. -# lineNumber - Where the scope changed. -# -sub AddToScopeRecord #(newScope, lineNumber) - { - my ($self, $scope, $lineNumber) = @_; - my $scopeRecord = $self->ScopeRecord(); - - if ($scope ne $scopeRecord->[-1]->Scope()) - { - if ($scopeRecord->[-1]->LineNumber() == $lineNumber) - { $scopeRecord->[-1]->SetScope($scope); } - else - { push @$scopeRecord, NaturalDocs::Languages::Advanced::ScopeChange->New($scope, $lineNumber); }; - }; - }; - - -# -# Function: CreateString -# -# Converts the specified tokens into a string and returns it. -# -# Parameters: -# -# startIndex - The starting index to convert. -# endIndex - The ending index, which is *not inclusive*. -# -# Returns: -# -# The string. -# -sub CreateString #(startIndex, endIndex) - { - my ($self, $startIndex, $endIndex) = @_; - my $tokens = $self->Tokens(); - - my $string; - - while ($startIndex < $endIndex && $startIndex < scalar @$tokens) - { - $string .= $tokens->[$startIndex]; - $startIndex++; - }; - - return $string; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Advanced/Scope.pm b/docs/doctool/Modules/NaturalDocs/Languages/Advanced/Scope.pm deleted file mode 100644 index 49defeac..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Advanced/Scope.pm +++ /dev/null @@ -1,95 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Advanced::Scope -# -############################################################################### -# -# A class used to store a scope level. -# -############################################################################### - -# 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::Languages::Advanced::Scope; - -# -# Constants: Implementation -# -# The object is implemented as a blessed arrayref. The constants below are used as indexes. -# -# CLOSING_SYMBOL - The closing symbol character of the scope. -# PACKAGE - The package <SymbolString> of the scope. -# USING - An arrayref of <SymbolStrings> for using statements, or undef if none. -# -use NaturalDocs::DefineMembers 'CLOSING_SYMBOL', 'PACKAGE', 'USING'; -# Dependency: New() depends on the order of these constants as well as that there is no inherited members. - - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# closingSymbol - The closing symbol character of the scope. -# package - The package <SymbolString> of the scope. -# using - An arrayref of using <SymbolStrings>, or undef if none. The contents of the array will be duplicated. -# -# If package is set to undef, it is assumed that it inherits the value of the previous scope on the stack. -# -sub New #(closingSymbol, package, using) - { - # Dependency: This depends on the order of the parameters matching the constants, and that there are no inherited - # members. - my $package = shift; - - my $object = [ @_ ]; - bless $object, $package; - - if (defined $object->[USING]) - { $object->[USING] = [ @{$object->[USING]} ]; }; - - return $object; - }; - - -# Function: ClosingSymbol -# Returns the closing symbol character of the scope. -sub ClosingSymbol - { return $_[0]->[CLOSING_SYMBOL]; }; - -# Function: Package -# Returns the package <SymbolString> of the scope, or undef if none. -sub Package - { return $_[0]->[PACKAGE]; }; - -# Function: SetPackage -# Sets the package <SymbolString> of the scope. -sub SetPackage #(package) - { $_[0]->[PACKAGE] = $_[1]; }; - -# Function: Using -# Returns an arrayref of <SymbolStrings> for using statements, or undef if none -sub Using - { return $_[0]->[USING]; }; - -# Function: AddUsing -# Adds a <SymbolString> to the <Using()> array. -sub AddUsing #(using) - { - my ($self, $using) = @_; - - if (!defined $self->[USING]) - { $self->[USING] = [ ]; }; - - push @{$self->[USING]}, $using; - }; - - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm b/docs/doctool/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm deleted file mode 100644 index 89b45ff4..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm +++ /dev/null @@ -1,70 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Advanced::ScopeChange -# -############################################################################### -# -# A class used to store a scope change. -# -############################################################################### - -# 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::Languages::Advanced::ScopeChange; - -# -# Constants: Implementation -# -# The object is implemented as a blessed arrayref. The constants below are used as indexes. -# -# SCOPE - The new scope <SymbolString>. -# LINE_NUMBER - The line number of the change. -# -use NaturalDocs::DefineMembers 'SCOPE', 'LINE_NUMBER'; -# Dependency: New() depends on the order of these constants as well as that there is no inherited members. - - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# scope - The <SymbolString> the scope was changed to. -# lineNumber - What line it occurred on. -# -sub New #(scope, lineNumber) - { - # Dependency: This depends on the order of the parameters matching the constants, and that there are no inherited - # members. - my $self = shift; - - my $object = [ @_ ]; - bless $object, $self; - - return $object; - }; - - -# Function: Scope -# Returns the <SymbolString> the scope was changed to. -sub Scope - { return $_[0]->[SCOPE]; }; - -# Function: SetScope -# Replaces the <SymbolString> the scope was changed to. -sub SetScope #(scope) - { $_[0]->[SCOPE] = $_[1]; }; - -# Function: LineNumber -# Returns the line number of the change. -sub LineNumber - { return $_[0]->[LINE_NUMBER]; }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Base.pm b/docs/doctool/Modules/NaturalDocs/Languages/Base.pm deleted file mode 100644 index e84ca2fd..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Base.pm +++ /dev/null @@ -1,743 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Base -# -############################################################################### -# -# A base class for all programming language parsers. -# -############################################################################### - -# 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::Languages::Base; - -use NaturalDocs::DefineMembers 'NAME', 'Name()', - 'EXTENSIONS', 'Extensions()', 'SetExtensions() duparrayref', - 'SHEBANG_STRINGS', 'ShebangStrings()', 'SetShebangStrings() duparrayref', - 'IGNORED_PREFIXES', - 'ENUM_VALUES'; - -use base 'Exporter'; -our @EXPORT = ('ENUM_GLOBAL', 'ENUM_UNDER_TYPE', 'ENUM_UNDER_PARENT'); - - -# -# Constants: EnumValuesType -# -# How enum values are handled in the language. -# -# ENUM_GLOBAL - Values are always global and thus 'value'. -# ENUM_UNDER_TYPE - Values are under the type in the hierarchy, and thus 'package.enum.value'. -# ENUM_UNDER_PARENT - Values are under the parent in the hierarchy, putting them on the same level as the enum itself. Thus -# 'package.value'. -# -use constant ENUM_GLOBAL => 1; -use constant ENUM_UNDER_TYPE => 2; -use constant ENUM_UNDER_PARENT => 3; - - -# -# Handle: SOURCEFILEHANDLE -# -# The handle of the source file currently being parsed. -# - - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# name - The name of the language. -# -sub New #(name) - { - my ($selfPackage, $name) = @_; - - my $object = [ ]; - - $object->[NAME] = $name; - - bless $object, $selfPackage; - return $object; - }; - - -# -# Functions: Members -# -# Name - Returns the language's name. -# Extensions - Returns an arrayref of the language's file extensions, or undef if none. -# SetExtensions - Replaces the arrayref of the language's file extensions. -# ShebangStrings - Returns an arrayref of the language's shebang strings, or undef if none. -# SetShebangStrings - Replaces the arrayref of the language's shebang strings. -# - -# -# Function: PackageSeparator -# Returns the language's package separator string. -# -sub PackageSeparator - { return '.'; }; - -# -# Function: PackageSeparatorWasSet -# Returns whether the language's package separator string was ever changed from the default. -# -sub PackageSeparatorWasSet - { return 0; }; - - -# -# Function: EnumValues -# Returns the <EnumValuesType> that describes how the language handles enums. -# -sub EnumValues - { return ENUM_GLOBAL; }; - - -# -# Function: IgnoredPrefixesFor -# -# Returns an arrayref of ignored prefixes for the passed <TopicType>, or undef if none. The array is sorted so that the longest -# prefixes are first. -# -sub IgnoredPrefixesFor #(type) - { - my ($self, $type) = @_; - - if (defined $self->[IGNORED_PREFIXES]) - { return $self->[IGNORED_PREFIXES]->{$type}; } - else - { return undef; }; - }; - - -# -# Function: SetIgnoredPrefixesFor -# -# Replaces the arrayref of ignored prefixes for the passed <TopicType>. -# -sub SetIgnoredPrefixesFor #(type, prefixes) - { - my ($self, $type, $prefixesRef) = @_; - - if (!defined $self->[IGNORED_PREFIXES]) - { $self->[IGNORED_PREFIXES] = { }; }; - - if (!defined $prefixesRef) - { delete $self->[IGNORED_PREFIXES]->{$type}; } - else - { - my $prefixes = [ @$prefixesRef ]; - - # Sort prefixes to be longest to shortest. - @$prefixes = sort { length $b <=> length $a } @$prefixes; - - $self->[IGNORED_PREFIXES]->{$type} = $prefixes; - }; - }; - - -# -# Function: HasIgnoredPrefixes -# -# Returns whether the language has any ignored prefixes at all. -# -sub HasIgnoredPrefixes - { return defined $_[0]->[IGNORED_PREFIXES]; }; - - -# -# Function: CopyIgnoredPrefixesOf -# -# Copies all the ignored prefix settings of the passed <NaturalDocs::Languages::Base> object. -# -sub CopyIgnoredPrefixesOf #(language) - { - my ($self, $language) = @_; - - if ($language->HasIgnoredPrefixes()) - { - $self->[IGNORED_PREFIXES] = { }; - - while (my ($topicType, $prefixes) = each %{$language->[IGNORED_PREFIXES]}) - { - $self->[IGNORED_PREFIXES]->{$topicType} = [ @$prefixes ]; - }; - }; - }; - - - -############################################################################### -# Group: Parsing Functions - - -# -# Function: ParseFile -# -# Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>. -# This *must* be defined by a subclass. -# -# Parameters: -# -# sourceFile - The <FileName> of the source file to parse. -# topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file. -# -# Returns: -# -# The array ( autoTopics, scopeRecord ). -# -# autoTopics - An arrayref of automatically generated <NaturalDocs::Parser::ParsedTopics> from the file, or undef if none. -# scopeRecord - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none. -# - - -# -# Function: ParsePrototype -# -# Parses the prototype and returns it as a <NaturalDocs::Languages::Prototype> object. -# -# Parameters: -# -# type - The <TopicType>. -# prototype - The text prototype. -# -# Returns: -# -# A <NaturalDocs::Languages::Prototype> object. -# -sub ParsePrototype #(type, prototype) - { - my ($self, $type, $prototype) = @_; - - if ($prototype !~ /\(.*[^ ].*\)/) - { - my $object = NaturalDocs::Languages::Prototype->New($prototype); - return $object; - }; - - - # Parse the parameters out of the prototype. - - my @tokens = $prototype =~ /([^\(\)\[\]\{\}\<\>\'\"\,\;]+|.)/g; - - my $parameter; - my @parameterLines; - - my @symbolStack; - my $finishedParameters; - - my ($beforeParameters, $afterParameters); - - foreach my $token (@tokens) - { - if ($finishedParameters) - { $afterParameters .= $token; } - - elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"') - { - if ($symbolStack[0] eq '(') - { $parameter .= $token; } - else - { $beforeParameters .= $token; }; - - if ($token eq $symbolStack[-1]) - { pop @symbolStack; }; - } - - elsif ($token =~ /^[\(\[\{\<\'\"]$/) - { - if ($symbolStack[0] eq '(') - { $parameter .= $token; } - else - { $beforeParameters .= $token; }; - - push @symbolStack, $token; - } - - elsif ( ($token eq ')' && $symbolStack[-1] eq '(') || - ($token eq ']' && $symbolStack[-1] eq '[') || - ($token eq '}' && $symbolStack[-1] eq '{') || - ($token eq '>' && $symbolStack[-1] eq '<') ) - { - if ($symbolStack[0] eq '(') - { - if ($token eq ')' && scalar @symbolStack == 1) - { - if ($parameter ne ' ') - { push @parameterLines, $parameter; }; - - $finishedParameters = 1; - $afterParameters .= $token; - } - else - { $parameter .= $token; }; - } - else - { - $beforeParameters .= $token; - }; - - pop @symbolStack; - } - - elsif ($token eq ',' || $token eq ';') - { - if ($symbolStack[0] eq '(') - { - if (scalar @symbolStack == 1) - { - push @parameterLines, $parameter . $token; - $parameter = undef; - } - else - { - $parameter .= $token; - }; - } - else - { - $beforeParameters .= $token; - }; - } - - else - { - if ($symbolStack[0] eq '(') - { $parameter .= $token; } - else - { $beforeParameters .= $token; }; - }; - }; - - foreach my $part (\$beforeParameters, \$afterParameters) - { - $$part =~ s/^ //; - $$part =~ s/ $//; - }; - - my $prototypeObject = NaturalDocs::Languages::Prototype->New($beforeParameters, $afterParameters); - - - # Parse the actual parameters. - - foreach my $parameterLine (@parameterLines) - { - $prototypeObject->AddParameter( $self->ParseParameterLine($parameterLine) ); - }; - - return $prototypeObject; - }; - - -# -# Function: ParseParameterLine -# -# Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object. -# -# This vesion assumes a C++ style line. If you need a Pascal style line, override this function to forward to -# <ParsePascalParameterLine()>. -# -# > Function(parameter, type parameter, type parameter = value); -# -sub ParseParameterLine #(line) - { - my ($self, $line) = @_; - - $line =~ s/^ //; - $line =~ s/ $//; - - my @tokens = $line =~ /([^ \(\)\{\}\[\]\<\>\'\"\=]+|.)/g; - - my @symbolStack; - my @parameterWords = ( undef ); - my ($defaultValue, $defaultValuePrefix, $inDefaultValue); - - foreach my $token (@tokens) - { - if ($inDefaultValue) - { $defaultValue .= $token; } - - elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"') - { - $parameterWords[-1] .= $token; - - if ($token eq $symbolStack[-1]) - { pop @symbolStack; }; - } - - elsif ($token =~ /^[\(\[\{\<\'\"]$/) - { - push @symbolStack, $token; - $parameterWords[-1] .= $token; - } - - elsif ( ($token eq ')' && $symbolStack[-1] eq '(') || - ($token eq ']' && $symbolStack[-1] eq '[') || - ($token eq '}' && $symbolStack[-1] eq '{') || - ($token eq '>' && $symbolStack[-1] eq '<') ) - { - pop @symbolStack; - $parameterWords[-1] .= $token; - } - - elsif ($token eq ' ') - { - if (!scalar @symbolStack) - { push @parameterWords, undef; } - else - { $parameterWords[-1] .= $token; }; - } - - elsif ($token eq '=') - { - if (!scalar @symbolStack) - { - $defaultValuePrefix = $token; - $inDefaultValue = 1; - } - else - { $parameterWords[-1] .= $token; }; - } - - else - { - $parameterWords[-1] .= $token; - }; - }; - - my ($name, $namePrefix, $type, $typePrefix); - - if (!$parameterWords[-1]) - { pop @parameterWords; }; - - $name = pop @parameterWords; - - if ($parameterWords[-1]=~ /([\*\&]+)$/) - { - $namePrefix = $1; - $parameterWords[-1] = substr($parameterWords[-1], 0, 0 - length($namePrefix)); - $parameterWords[-1] =~ s/ $//; - - if (!$parameterWords[-1]) - { pop @parameterWords; }; - } - elsif ($name =~ /^([\*\&]+)/) - { - $namePrefix = $1; - $name = substr($name, length($namePrefix)); - $name =~ s/^ //; - }; - - $type = pop @parameterWords; - $typePrefix = join(' ', @parameterWords); - - if ($typePrefix) - { $typePrefix .= ' '; }; - - if ($type =~ /^([a-z0-9_\:\.]+(?:\.|\:\:))[a-z0-9_]/i) - { - my $attachedTypePrefix = $1; - - $typePrefix .= $attachedTypePrefix; - $type = substr($type, length($attachedTypePrefix)); - }; - - $defaultValue =~ s/ $//; - - return NaturalDocs::Languages::Prototype::Parameter->New($type, $typePrefix, $name, $namePrefix, - $defaultValue, $defaultValuePrefix); - }; - - -# -# Function: ParsePascalParameterLine -# -# Parses a Pascal-like prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object. -# Pascal lines are as follows: -# -# > Function (name: type; name, name: type := value) -# -# Also supports ActionScript lines -# -# > Function (name: type, name, name: type = value) -# -sub ParsePascalParameterLine #(line) - { - my ($self, $line) = @_; - - $line =~ s/^ //; - $line =~ s/ $//; - - my @tokens = $line =~ /([^\(\)\{\}\[\]\<\>\'\"\=\:]+|\:\=|.)/g; - my ($type, $name, $defaultValue, $defaultValuePrefix, $afterName, $afterDefaultValue); - my @symbolStack; - - foreach my $token (@tokens) - { - if ($afterDefaultValue) - { $defaultValue .= $token; } - - elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"') - { - if ($afterName) - { $type .= $token; } - else - { $name .= $token; }; - - if ($token eq $symbolStack[-1]) - { pop @symbolStack; }; - } - - elsif ($token =~ /^[\(\[\{\<\'\"]$/) - { - push @symbolStack, $token; - - if ($afterName) - { $type .= $token; } - else - { $name .= $token; }; - } - - elsif ( ($token eq ')' && $symbolStack[-1] eq '(') || - ($token eq ']' && $symbolStack[-1] eq '[') || - ($token eq '}' && $symbolStack[-1] eq '{') || - ($token eq '>' && $symbolStack[-1] eq '<') ) - { - pop @symbolStack; - - if ($afterName) - { $type .= $token; } - else - { $name .= $token; }; - } - - elsif ($afterName) - { - if (($token eq ':=' || $token eq '=') && !scalar @symbolStack) - { - $defaultValuePrefix = $token; - $afterDefaultValue = 1; - } - else - { $type .= $token; }; - } - - elsif ($token eq ':' && !scalar @symbolStack) - { - $name .= $token; - $afterName = 1; - } - - else - { $name .= $token; }; - }; - - foreach my $part (\$type, \$name, \$defaultValue) - { - $$part =~ s/^ //; - $$part =~ s/ $//; - }; - - return NaturalDocs::Languages::Prototype::Parameter->New($type, undef, $name, undef, $defaultValue, $defaultValuePrefix); - }; - - -# -# Function: TypeBeforeParameter -# -# Returns whether the type appears before the parameter in prototypes. -# -# For example, it does in C++ -# > void Function (int a, int b) -# -# but does not in Pascal -# > function Function (a: int; b, c: int) -# -sub TypeBeforeParameter - { - return 1; - }; - - - -# -# Function: IgnoredPrefixLength -# -# Returns the length of the prefix that should be ignored in the index, or zero if none. -# -# Parameters: -# -# name - The name of the symbol. -# type - The symbol's <TopicType>. -# -# Returns: -# -# The length of the prefix to ignore, or zero if none. -# -sub IgnoredPrefixLength #(name, type) - { - my ($self, $name, $type) = @_; - - foreach my $prefixes ($self->IgnoredPrefixesFor($type), $self->IgnoredPrefixesFor(::TOPIC_GENERAL())) - { - if (defined $prefixes) - { - foreach my $prefix (@$prefixes) - { - if (substr($name, 0, length($prefix)) eq $prefix) - { return length($prefix); }; - }; - }; - }; - - return 0; - }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: StripOpeningSymbols -# -# Determines if the line starts with any of the passed symbols, and if so, replaces it with spaces. This only happens -# if the only thing before it on the line is whitespace. -# -# Parameters: -# -# lineRef - A reference to the line to check. -# symbols - An arrayref of the symbols to check for. -# -# Returns: -# -# If the line starts with any of the passed comment symbols, it will replace it in the line with spaces and return the symbol. -# If the line doesn't, it will leave the line alone and return undef. -# -sub StripOpeningSymbols #(lineRef, symbols) - { - my ($self, $lineRef, $symbols) = @_; - - if (!defined $symbols) - { return undef; }; - - my ($index, $symbol) = ::FindFirstSymbol($$lineRef, $symbols); - - if ($index != -1 && substr($$lineRef, 0, $index) =~ /^[ \t]*$/) - { - return substr($$lineRef, $index, length($symbol), ' ' x length($symbol)); - }; - - return undef; - }; - - -# -# Function: StripOpeningBlockSymbols -# -# Determines if the line starts with any of the opening symbols in the passed symbol pairs, and if so, replaces it with spaces. -# This only happens if the only thing before it on the line is whitespace. -# -# Parameters: -# -# lineRef - A reference to the line to check. -# symbolPairs - An arrayref of the symbol pairs to check for. Pairs are specified as two consecutive array entries, with the -# opening symbol first. -# -# Returns: -# -# If the line starts with any of the opening symbols, it will replace it in the line with spaces and return the closing symbol. -# If the line doesn't, it will leave the line alone and return undef. -# -sub StripOpeningBlockSymbols #(lineRef, symbolPairs) - { - my ($self, $lineRef, $symbolPairs) = @_; - - if (!defined $symbolPairs) - { return undef; }; - - for (my $i = 0; $i < scalar @$symbolPairs; $i += 2) - { - my $index = index($$lineRef, $symbolPairs->[$i]); - - if ($index != -1 && substr($$lineRef, 0, $index) =~ /^[ \t]*$/) - { - substr($$lineRef, $index, length($symbolPairs->[$i]), ' ' x length($symbolPairs->[$i])); - return $symbolPairs->[$i + 1]; - }; - }; - - return undef; - }; - - -# -# Function: StripClosingSymbol -# -# Determines if the line contains a symbol, and if so, truncates it just before the symbol. -# -# Parameters: -# -# lineRef - A reference to the line to check. -# symbol - The symbol to check for. -# -# Returns: -# -# The remainder of the line, or undef if the symbol was not found. -# -sub StripClosingSymbol #(lineRef, symbol) - { - my ($self, $lineRef, $symbol) = @_; - - my $index = index($$lineRef, $symbol); - - if ($index != -1) - { - my $lineRemainder = substr($$lineRef, $index + length($symbol)); - $$lineRef = substr($$lineRef, 0, $index); - - return $lineRemainder; - } - else - { return undef; }; - }; - - -# -# Function: NormalizePrototype -# -# Normalizes a prototype. Specifically, condenses spaces, tabs, and line breaks into single spaces and removes leading and -# trailing ones. -# -# Parameters: -# -# prototype - The original prototype string. -# -# Returns: -# -# The normalized prototype. -# -sub NormalizePrototype #(prototype) - { - my ($self, $prototype) = @_; - - $prototype =~ tr/ \t\r\n/ /s; - $prototype =~ s/^ //; - $prototype =~ s/ $//; - - return $prototype; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/CSharp.pm b/docs/doctool/Modules/NaturalDocs/Languages/CSharp.pm deleted file mode 100644 index 72f2b871..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/CSharp.pm +++ /dev/null @@ -1,1215 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::CSharp -# -############################################################################### -# -# A subclass to handle the language variations of C#. -# -# -# Topic: Language Support -# -# Supported: -# -# - Classes -# - Namespaces (no topic generated) -# - Functions -# - Constructors and Destructors -# - Properties -# - Indexers -# - Operators -# - Delegates -# - Variables -# - Constants -# - Events -# -# Not supported yet: -# -# - Enums -# - Using -# - Using alias -# -############################################################################### - -# 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::Languages::CSharp; - -use base 'NaturalDocs::Languages::Advanced'; - - -############################################################################### -# Group: Package Variables - -# -# hash: classKeywords -# An existence hash of all the acceptable class keywords. The keys are in all lowercase. -# -my %classKeywords = ( 'class' => 1, - 'struct' => 1, - 'interface' => 1 ); - -# -# hash: classModifiers -# An existence hash of all the acceptable class modifiers. The keys are in all lowercase. -# -my %classModifiers = ( 'new' => 1, - 'public' => 1, - 'protected' => 1, - 'internal' => 1, - 'private' => 1, - 'abstract' => 1, - 'sealed' => 1, - 'unsafe' => 1 ); - -# -# hash: functionModifiers -# An existence hash of all the acceptable function modifiers. Also applies to properties. Also encompasses those for operators -# and indexers, but have more than are valid for them. The keys are in all lowercase. -# -my %functionModifiers = ( 'new' => 1, - 'public' => 1, - 'protected' => 1, - 'internal' => 1, - 'private' => 1, - 'static' => 1, - 'virtual' => 1, - 'sealed' => 1, - 'override' => 1, - 'abstract' => 1, - 'extern' => 1, - 'unsafe' => 1 ); - -# -# hash: variableModifiers -# An existence hash of all the acceptable variable modifiers. The keys are in all lowercase. -# -my %variableModifiers = ( 'new' => 1, - 'public' => 1, - 'protected' => 1, - 'internal' => 1, - 'private' => 1, - 'static' => 1, - 'readonly' => 1, - 'volatile' => 1, - 'unsafe' => 1 ); - -# -# hash: impossibleTypeWords -# An existence hash of all the reserved words that cannot be in a type. This includes 'enum' and all modifiers. The keys are in -# all lowercase. -# -my %impossibleTypeWords = ( 'abstract' => 1, 'as' => 1, 'base' => 1, 'break' => 1, 'case' => 1, 'catch' => 1, - 'checked' => 1, 'class' => 1, 'const' => 1, 'continue' => 1, 'default' => 1, 'delegate' => 1, - 'do' => 1, 'else' => 1, 'enum' => 1, 'event' => 1, 'explicit' => 1, 'extern' => 1, - 'false' => 1, 'finally' => 1, 'fixed' => 1, 'for' => 1, 'foreach' => 1, 'goto' => 1, 'if' => 1, - 'implicit' => 1, 'in' => 1, 'interface' => 1, 'internal' => 1, 'is' => 1, 'lock' => 1, - 'namespace' => 1, 'new' => 1, 'null' => 1, 'operator' => 1, 'out' => 1, 'override' => 1, - 'params' => 1, 'private' => 1, 'protected' => 1, 'public' => 1, 'readonly' => 1, 'ref' => 1, - 'return' => 1, 'sealed' => 1, 'sizeof' => 1, 'stackalloc' => 1, 'static' => 1, - 'struct' => 1, 'switch' => 1, 'this' => 1, 'throw' => 1, 'true' => 1, 'try' => 1, 'typeof' => 1, - 'unchecked' => 1, 'unsafe' => 1, 'using' => 1, 'virtual' => 1, 'volatile' => 1, 'while' => 1 ); -# Deleted from the list: object, string, bool, decimal, sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, void - - - -############################################################################### -# Group: Interface Functions - - -# -# Function: PackageSeparator -# Returns the package separator symbol. -# -sub PackageSeparator - { return '.'; }; - - -# -# Function: EnumValues -# Returns the <EnumValuesType> that describes how the language handles enums. -# -sub EnumValues - { return ::ENUM_UNDER_TYPE(); }; - - -# -# Function: ParseFile -# -# Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>. -# -# Parameters: -# -# sourceFile - The <FileName> to parse. -# topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file. -# -# Returns: -# -# The array ( autoTopics, scopeRecord ). -# -# autoTopics - An arrayref of automatically generated topics from the file, or undef if none. -# scopeRecord - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none. -# -sub ParseFile #(sourceFile, topicsList) - { - my ($self, $sourceFile, $topicsList) = @_; - - $self->ParseForCommentsAndTokens($sourceFile, [ '//' ], [ '/*', '*/' ] ); - - my $tokens = $self->Tokens(); - my $index = 0; - my $lineNumber = 1; - - while ($index < scalar @$tokens) - { - if ($self->TryToSkipWhitespace(\$index, \$lineNumber) || - $self->TryToGetNamespace(\$index, \$lineNumber) || - $self->TryToGetClass(\$index, \$lineNumber) || - $self->TryToGetFunction(\$index, \$lineNumber) || - $self->TryToGetOverloadedOperator(\$index, \$lineNumber) || - $self->TryToGetVariable(\$index, \$lineNumber) ) - { - # The functions above will handle everything. - } - - elsif ($tokens->[$index] eq '{') - { - $self->StartScope('}', $lineNumber, undef, undef, undef); - $index++; - } - - elsif ($tokens->[$index] eq '}') - { - if ($self->ClosingScopeSymbol() eq '}') - { $self->EndScope($lineNumber); }; - - $index++; - } - - else - { - $self->SkipRestOfStatement(\$index, \$lineNumber); - }; - }; - - - # Don't need to keep these around. - $self->ClearTokens(); - - - my $autoTopics = $self->AutoTopics(); - - my $scopeRecord = $self->ScopeRecord(); - if (defined $scopeRecord && !scalar @$scopeRecord) - { $scopeRecord = undef; }; - - return ( $autoTopics, $scopeRecord ); - }; - - - -############################################################################### -# Group: Statement Parsing Functions -# All functions here assume that the current position is at the beginning of a statement. -# -# Note for developers: I am well aware that the code in these functions do not check if we're past the end of the tokens as -# often as it should. We're making use of the fact that Perl will always return undef in these cases to keep the code simpler. - - -# -# Function: TryToGetNamespace -# -# Determines whether the position is at a namespace declaration statement, and if so, adjusts the scope, skips it, and returns -# true. -# -# Why no topic?: -# -# The main reason we don't create a Natural Docs topic for a namespace is because in order to declare class A.B.C in C#, -# you must do this: -# -# > namespace A.B -# > { -# > class C -# > { ... } -# > } -# -# That would result in a namespace topic whose only purpose is really to qualify C. It would take the default page title, and -# thus the default menu title. So if you have files for A.B.X, A.B.Y, and A.B.Z, they all will appear as A.B on the menu. -# -# If something actually appears in the namespace besides a class, it will be handled by -# <NaturalDocs::Parser->AddPackageDelineators()>. That function will add a package topic to correct the scope. -# -# If the user actually documented it, it will still appear because of the manual topic. -# -sub TryToGetNamespace #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - if (lc($tokens->[$$indexRef]) ne 'namespace') - { return undef; }; - - my $index = $$indexRef + 1; - my $lineNumber = $$lineNumberRef; - - if (!$self->TryToSkipWhitespace(\$index, \$lineNumber)) - { return undef; }; - - my $name; - - while ($tokens->[$index] =~ /^[a-z_\.\@]/i) - { - $name .= $tokens->[$index]; - $index++; - }; - - if (!defined $name) - { return undef; }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if ($tokens->[$index] ne '{') - { return undef; }; - - $index++; - - - # We found a valid one if we made it this far. - - my $autoTopic = NaturalDocs::Parser::ParsedTopic->New(::TOPIC_CLASS(), $name, - $self->CurrentScope(), undef, - undef, - undef, undef, $$lineNumberRef); - - # We don't add an auto-topic for namespaces. See the function documentation above. - - NaturalDocs::Parser->OnClass($autoTopic->Package()); - - $self->StartScope('}', $lineNumber, $autoTopic->Package()); - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - -# -# Function: TryToGetClass -# -# Determines whether the position is at a class declaration statement, and if so, generates a topic for it, skips it, and -# returns true. -# -# Supported Syntaxes: -# -# - Classes -# - Structs -# - Interfaces -# -sub TryToGetClass #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - if ($self->TryToSkipAttributes(\$index, \$lineNumber)) - { $self->TryToSkipWhitespace(\$index, \$lineNumber); } - - my @modifiers; - - while ($tokens->[$index] =~ /^[a-z]/i && - !exists $classKeywords{lc($tokens->[$index])} && - exists $classModifiers{lc($tokens->[$index])} ) - { - push @modifiers, lc($tokens->[$index]); - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - if (!exists $classKeywords{lc($tokens->[$index])}) - { return undef; }; - - my $lcClassKeyword = lc($tokens->[$index]); - - $index++; - - if (!$self->TryToSkipWhitespace(\$index, \$lineNumber)) - { return undef; }; - - my $name; - - while ($tokens->[$index] =~ /^[a-z_\@]/i) - { - $name .= $tokens->[$index]; - $index++; - }; - - if (!defined $name) - { return undef; }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my @parents; - - if ($tokens->[$index] eq ':') - { - do - { - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my $parentName; - - while ($tokens->[$index] =~ /^[a-z_\.\@]/i) - { - $parentName .= $tokens->[$index]; - $index++; - }; - - if (!defined $parentName) - { return undef; }; - - push @parents, NaturalDocs::SymbolString->FromText($parentName); - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - } - while ($tokens->[$index] eq ',') - }; - - if ($tokens->[$index] ne '{') - { return undef; }; - - $index++; - - - # If we made it this far, we have a valid class declaration. - - my @scopeIdentifiers = NaturalDocs::SymbolString->IdentifiersOf($self->CurrentScope()); - $name = join('.', @scopeIdentifiers, $name); - - my $topicType; - - if ($lcClassKeyword eq 'interface') - { $topicType = ::TOPIC_INTERFACE(); } - else - { $topicType = ::TOPIC_CLASS(); }; - - my $autoTopic = NaturalDocs::Parser::ParsedTopic->New($topicType, $name, - undef, undef, - undef, - undef, undef, $$lineNumberRef); - - $self->AddAutoTopic($autoTopic); - NaturalDocs::Parser->OnClass($autoTopic->Package()); - - foreach my $parent (@parents) - { - NaturalDocs::Parser->OnClassParent($autoTopic->Package(), $parent, $self->CurrentScope(), undef, - ::RESOLVE_RELATIVE()); - }; - - $self->StartScope('}', $lineNumber, $autoTopic->Package()); - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - -# -# Function: TryToGetFunction -# -# Determines if the position is on a function declaration, and if so, generates a topic for it, skips it, and returns true. -# -# Supported Syntaxes: -# -# - Functions -# - Constructors -# - Destructors -# - Properties -# - Indexers -# - Delegates -# - Events -# -sub TryToGetFunction #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - if ($self->TryToSkipAttributes(\$index, \$lineNumber)) - { $self->TryToSkipWhitespace(\$index, \$lineNumber); }; - - my $startIndex = $index; - my $startLine = $lineNumber; - - my @modifiers; - - while ($tokens->[$index] =~ /^[a-z]/i && - exists $functionModifiers{lc($tokens->[$index])} ) - { - push @modifiers, lc($tokens->[$index]); - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - my $isDelegate; - my $isEvent; - - if (lc($tokens->[$index]) eq 'delegate') - { - $isDelegate = 1; - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - } - elsif (lc($tokens->[$index]) eq 'event') - { - $isEvent = 1; - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - my $returnType = $self->TryToGetType(\$index, \$lineNumber); - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my $name; - my $lastNameWord; - - while ($tokens->[$index] =~ /^[a-z\_\@\.\~]/i) - { - $name .= $tokens->[$index]; - $lastNameWord = $tokens->[$index]; - $index++; - }; - - if (!defined $name) - { - # Constructors and destructors don't have return types. It's possible their names were mistaken for the return type. - if (defined $returnType) - { - $name = $returnType; - $returnType = undef; - - $name =~ /([a-z0-9_]+)$/i; - $lastNameWord = $1; - } - else - { return undef; }; - }; - - # If there's no return type, make sure it's a constructor or destructor. - if (!defined $returnType) - { - my @identifiers = NaturalDocs::SymbolString->IdentifiersOf( $self->CurrentScope() ); - - if ($lastNameWord ne $identifiers[-1]) - { return undef; }; - }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - - # Skip the brackets on indexers. - if ($tokens->[$index] eq '[' && lc($lastNameWord) eq 'this') - { - # This should jump the brackets completely. - $self->GenericSkip(\$index, \$lineNumber); - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - $name .= '[]'; - }; - - - # Properties, indexers, events with braces - - if ($tokens->[$index] eq '{') - { - my $prototype = $self->CreateString($startIndex, $index); - - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my ($aWord, $bWord, $hasA, $hasB); - - if ($isEvent) - { - $aWord = 'add'; - $bWord = 'remove'; - } - else - { - $aWord = 'get'; - $bWord = 'set'; - }; - - while ($index < scalar @$tokens) - { - if ($self->TryToSkipAttributes(\$index, \$lineNumber)) - { $self->TryToSkipWhitespace(\$index, \$lineNumber); }; - - if (lc($tokens->[$index]) eq $aWord) - { $hasA = 1; } - elsif (lc($tokens->[$index]) eq $bWord) - { $hasB = 1; } - elsif ($tokens->[$index] eq '}') - { - $index++; - last; - }; - - $self->SkipRestOfStatement(\$index, \$lineNumber); - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - if ($hasA && $hasB) - { $prototype .= ' { ' . $aWord . ', ' . $bWord . ' }'; } - elsif ($hasA) - { $prototype .= ' { ' . $aWord . ' }'; } - elsif ($hasB) - { $prototype .= ' { ' . $bWord . ' }'; }; - - $prototype = $self->NormalizePrototype($prototype); - - my $topicType = ( $isEvent ? ::TOPIC_EVENT() : ::TOPIC_PROPERTY() ); - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($topicType, $name, - $self->CurrentScope(), undef, - $prototype, - undef, undef, $startLine)); - } - - - # Functions, constructors, destructors, delegates. - - elsif ($tokens->[$index] eq '(') - { - # This should jump the parenthesis completely. - $self->GenericSkip(\$index, \$lineNumber); - - my $topicType = ( $isDelegate ? ::TOPIC_DELEGATE() : ::TOPIC_FUNCTION() ); - my $prototype = $self->NormalizePrototype( $self->CreateString($startIndex, $index) ); - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($topicType, $name, - $self->CurrentScope(), undef, - $prototype, - undef, undef, $startLine)); - - $self->SkipRestOfStatement(\$index, \$lineNumber); - } - - - # Events without braces - - elsif ($isEvent && $tokens->[$index] eq ';') - { - my $prototype = $self->NormalizePrototype( $self->CreateString($startIndex, $index) ); - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_EVENT(), $name, - $self->CurrentScope(), undef, - $prototype, - undef, undef, $startLine)); - $index++; - } - - else - { return undef; }; - - - # We succeeded if we got this far. - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - -# -# Function: TryToGetOverloadedOperator -# -# Determines if the position is on an operator overload declaration, and if so, generates a topic for it, skips it, and returns true. -# -sub TryToGetOverloadedOperator #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - if ($self->TryToSkipAttributes(\$index, \$lineNumber)) - { $self->TryToSkipWhitespace(\$index, \$lineNumber); }; - - my $startIndex = $index; - my $startLine = $lineNumber; - - my @modifiers; - - while ($tokens->[$index] =~ /^[a-z]/i && - exists $functionModifiers{lc($tokens->[$index])} ) - { - push @modifiers, lc($tokens->[$index]); - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - - my $name; - - - # Casting operators. - - if (lc($tokens->[$index]) eq 'implicit' || lc($tokens->[$index]) eq 'explicit') - { - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if (lc($tokens->[$index]) ne 'operator') - { return undef; }; - - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - $name = $self->TryToGetType(\$index, \$lineNumber); - - if (!defined $name) - { return undef; }; - } - - - # Symbol operators. - - else - { - if (!$self->TryToGetType(\$index, \$lineNumber)) - { return undef; }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if (lc($tokens->[$index]) ne 'operator') - { return undef; }; - - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if (lc($tokens->[$index]) eq 'true' || lc($tokens->[$index]) eq 'false') - { - $name = $tokens->[$index]; - $index++; - } - else - { - while ($tokens->[$index] =~ /^[\+\-\!\~\*\/\%\&\|\^\<\>\=]$/) - { - $name .= $tokens->[$index]; - $index++; - }; - }; - }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if ($tokens->[$index] ne '(') - { return undef; }; - - # This should skip the parenthesis completely. - $self->GenericSkip(\$index, \$lineNumber); - - my $prototype = $self->NormalizePrototype( $self->CreateString($startIndex, $index) ); - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_FUNCTION(), 'operator ' . $name, - $self->CurrentScope(), undef, - $prototype, - undef, undef, $startLine)); - - $self->SkipRestOfStatement(\$index, \$lineNumber); - - - # We succeeded if we got this far. - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - -# -# Function: TryToGetVariable -# -# Determines if the position is on a variable declaration statement, and if so, generates a topic for each variable, skips the -# statement, and returns true. -# -# Supported Syntaxes: -# -# - Variables -# - Constants -# -sub TryToGetVariable #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - if ($self->TryToSkipAttributes(\$index, \$lineNumber)) - { $self->TryToSkipWhitespace(\$index, \$lineNumber); }; - - my $startIndex = $index; - my $startLine = $lineNumber; - - my @modifiers; - - while ($tokens->[$index] =~ /^[a-z]/i && - exists $variableModifiers{lc($tokens->[$index])} ) - { - push @modifiers, lc($tokens->[$index]); - $index++; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - my $type; - if (lc($tokens->[$index]) eq 'const') - { - $type = ::TOPIC_CONSTANT(); - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - } - else - { - $type = ::TOPIC_VARIABLE(); - }; - - if (!$self->TryToGetType(\$index, \$lineNumber)) - { return undef; }; - - my $endTypeIndex = $index; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my @names; - - for (;;) - { - my $name; - - while ($tokens->[$index] =~ /^[a-z\@\_]/i) - { - $name .= $tokens->[$index]; - $index++; - }; - - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if ($tokens->[$index] eq '=') - { - do - { - $self->GenericSkip(\$index, \$lineNumber); - } - while ($tokens->[$index] ne ',' && $tokens->[$index] ne ';'); - }; - - push @names, $name; - - if ($tokens->[$index] eq ';') - { - $index++; - last; - } - elsif ($tokens->[$index] eq ',') - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - } - else - { return undef; }; - }; - - - # We succeeded if we got this far. - - my $prototypePrefix = $self->CreateString($startIndex, $endTypeIndex); - - foreach my $name (@names) - { - my $prototype = $self->NormalizePrototype( $prototypePrefix . ' ' . $name ); - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New($type, $name, - $self->CurrentScope(), undef, - $prototype, - undef, undef, $startLine)); - }; - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - - -# -# Function: TryToGetType -# -# Determines if the position is on a type identifier, and if so, skips it and returns it as a string. This function does _not_ allow -# modifiers. You must take care of those beforehand. -# -sub TryToGetType #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $name; - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - while ($tokens->[$index] =~ /^[a-z\@\.\_]/i) - { - if (exists $impossibleTypeWords{ lc($tokens->[$index]) } && $name !~ /\@$/) - { return undef; }; - - $name .= $tokens->[$index]; - $index++; - }; - - if (!defined $name) - { return undef; }; - - while ($tokens->[$index] eq '[') - { - $name .= '['; - $index++; - - while ($tokens->[$index] eq ',') - { - $name .= ','; - $index++; - }; - - if ($tokens->[$index] eq ']') - { - $name .= ']'; - $index++; - } - else - { return undef; } - }; - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return $name; - }; - - - -############################################################################### -# Group: Low Level Parsing Functions - - -# -# Function: GenericSkip -# -# Advances the position one place through general code. -# -# - If the position is on a string, it will skip it completely. -# - If the position is on an opening symbol, it will skip until the past the closing symbol. -# - If the position is on whitespace (including comments and preprocessing directives), it will skip it completely. -# - Otherwise it skips one token. -# -# Parameters: -# -# indexRef - A reference to the current index. -# lineNumberRef - A reference to the current line number. -# -sub GenericSkip #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - # We can ignore the scope stack because we're just skipping everything without parsing, and we need recursion anyway. - if ($tokens->[$$indexRef] eq '{') - { - $$indexRef++; - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}'); - } - elsif ($tokens->[$$indexRef] eq '(') - { - $$indexRef++; - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ')'); - } - elsif ($tokens->[$$indexRef] eq '[') - { - $$indexRef++; - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ']'); - } - - elsif ($self->TryToSkipWhitespace($indexRef, $lineNumberRef) || - $self->TryToSkipString($indexRef, $lineNumberRef)) - { - } - - else - { $$indexRef++; }; - }; - - -# -# Function: GenericSkipUntilAfter -# -# Advances the position via <GenericSkip()> until a specific token is reached and passed. -# -sub GenericSkipUntilAfter #(indexRef, lineNumberRef, token) - { - my ($self, $indexRef, $lineNumberRef, $token) = @_; - my $tokens = $self->Tokens(); - - while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne $token) - { $self->GenericSkip($indexRef, $lineNumberRef); }; - - if ($tokens->[$$indexRef] eq "\n") - { $$lineNumberRef++; }; - $$indexRef++; - }; - - -# -# Function: SkipRestOfStatement -# -# Advances the position via <GenericSkip()> until after the end of the current statement, which is defined as a semicolon or -# a brace group. Of course, either of those appearing inside parenthesis, a nested brace group, etc. don't count. -# -sub SkipRestOfStatement #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - while ($$indexRef < scalar @$tokens && - $tokens->[$$indexRef] ne ';' && - $tokens->[$$indexRef] ne '{') - { - $self->GenericSkip($indexRef, $lineNumberRef); - }; - - if ($tokens->[$$indexRef] eq ';') - { $$indexRef++; } - elsif ($tokens->[$$indexRef] eq '{') - { $self->GenericSkip($indexRef, $lineNumberRef); }; - }; - - -# -# Function: TryToSkipString -# If the current position is on a string delimiter, skip past the string and return true. -# -# Parameters: -# -# indexRef - A reference to the index of the position to start at. -# lineNumberRef - A reference to the line number of the position. -# -# Returns: -# -# Whether the position was at a string. -# -# Syntax Support: -# -# - Supports quotes, apostrophes, and at-quotes. -# -sub TryToSkipString #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - # The three string delimiters. All three are Perl variables when preceded by a dollar sign. - if ($self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '\'') || - $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '"') ) - { - return 1; - } - elsif ($tokens->[$$indexRef] eq '@' && $tokens->[$$indexRef+1] eq '"') - { - $$indexRef += 2; - - # We need to do at-strings manually because backslash characters are accepted as regular characters, and two consecutive - # quotes are accepted as well. - - while ($$indexRef < scalar @$tokens && !($tokens->[$$indexRef] eq '"' && $tokens->[$$indexRef+1] ne '"') ) - { - if ($tokens->[$$indexRef] eq '"') - { - # This is safe because the while condition will only let through quote pairs. - $$indexRef += 2; - } - elsif ($tokens->[$$indexRef] eq "\n") - { - $$indexRef++; - $$lineNumberRef++; - } - else - { - $$indexRef++; - }; - }; - - # Skip the closing quote. - if ($$indexRef < scalar @$tokens) - { $$indexRef++; }; - - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToSkipAttributes -# If the current position is on an attribute section, skip it and return true. Skips multiple attribute sections if they appear -# consecutively. -# -sub TryToSkipAttributes #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $success; - - while ($tokens->[$$indexRef] eq '[') - { - $success = 1; - $$indexRef++; - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ']'); - $self->TryToSkipWhitespace($indexRef, $lineNumberRef); - }; - - return $success; - }; - - -# -# Function: TryToSkipWhitespace -# If the current position is on a whitespace token, a line break token, a comment, or a preprocessing directive, it skips them -# and returns true. If there are a number of these in a row, it skips them all. -# -sub TryToSkipWhitespace #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $result; - - while ($$indexRef < scalar @$tokens) - { - if ($tokens->[$$indexRef] =~ /^[ \t]/) - { - $$indexRef++; - $result = 1; - } - elsif ($tokens->[$$indexRef] eq "\n") - { - $$indexRef++; - $$lineNumberRef++; - $result = 1; - } - elsif ($self->TryToSkipComment($indexRef, $lineNumberRef) || - $self->TryToSkipPreprocessingDirective($indexRef, $lineNumberRef)) - { - $result = 1; - } - else - { last; }; - }; - - return $result; - }; - - -# -# Function: TryToSkipComment -# If the current position is on a comment, skip past it and return true. -# -sub TryToSkipComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - - return ( $self->TryToSkipLineComment($indexRef, $lineNumberRef) || - $self->TryToSkipMultilineComment($indexRef, $lineNumberRef) ); - }; - - -# -# Function: TryToSkipLineComment -# If the current position is on a line comment symbol, skip past it and return true. -# -sub TryToSkipLineComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - if ($tokens->[$$indexRef] eq '/' && $tokens->[$$indexRef+1] eq '/') - { - $self->SkipRestOfLine($indexRef, $lineNumberRef); - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToSkipMultilineComment -# If the current position is on an opening comment symbol, skip past it and return true. -# -sub TryToSkipMultilineComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - if ($tokens->[$$indexRef] eq '/' && $tokens->[$$indexRef+1] eq '*') - { - $self->SkipUntilAfter($indexRef, $lineNumberRef, '*', '/'); - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToSkipPreprocessingDirective -# If the current position is on a preprocessing directive, skip past it and return true. -# -sub TryToSkipPreprocessingDirective #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - if ($tokens->[$$indexRef] eq '#' && $self->IsFirstLineToken($$indexRef)) - { - $self->SkipRestOfLine($indexRef, $lineNumberRef); - return 1; - } - else - { return undef; }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/PLSQL.pm b/docs/doctool/Modules/NaturalDocs/Languages/PLSQL.pm deleted file mode 100644 index b713b323..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/PLSQL.pm +++ /dev/null @@ -1,313 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::PLSQL -# -############################################################################### -# -# A subclass to handle the language variations of PL/SQL. -# -############################################################################### - -# 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::Languages::PLSQL; - -use base 'NaturalDocs::Languages::Simple'; - - -# -# Function: OnPrototypeEnd -# -# Microsoft's SQL specifies parameters as shown below. -# -# > CREATE PROCEDURE Test @as int, @foo int AS ... -# -# Having a parameter @is or @as is perfectly valid even though those words are also used to end the prototype. We need to -# ignore text-based enders preceded by an at sign. Also note that it does not have parenthesis for parameter lists. We need to -# skip all commas if the prototype doesn't have parenthesis but does have @ characters. -# -# Parameters: -# -# type - The <TopicType> of the prototype. -# prototypeRef - A reference to the prototype so far, minus the ender in dispute. -# ender - The ender symbol. -# -# Returns: -# -# ENDER_ACCEPT - The ender is accepted and the prototype is finished. -# ENDER_IGNORE - The ender is rejected and parsing should continue. Note that the prototype will be rejected as a whole -# if all enders are ignored before reaching the end of the code. -# ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is. However, the prototype might -# also continue on so continue parsing. If there is no accepted ender between here and -# the end of the code this version will be accepted instead. -# ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed. Use the last accepted -# version and end parsing. -# -sub OnPrototypeEnd #(type, prototypeRef, ender) - { - my ($self, $type, $prototypeRef, $ender) = @_; - - if ($ender =~ /^[a-z]+$/i && substr($$prototypeRef, -1) eq '@') - { return ::ENDER_IGNORE(); } - - elsif ($type eq ::TOPIC_FUNCTION() && $ender eq ',') - { - if ($$prototypeRef =~ /^[^\(]*\@/) - { return ::ENDER_IGNORE(); } - else - { return ::ENDER_ACCEPT(); }; - } - - else - { return ::ENDER_ACCEPT(); }; - }; - - -# -# Function: ParsePrototype -# -# Overridden to handle Microsoft's parenthesisless version. Otherwise just throws to the parent. -# -# Parameters: -# -# type - The <TopicType>. -# prototype - The text prototype. -# -# Returns: -# -# A <NaturalDocs::Languages::Prototype> object. -# -sub ParsePrototype #(type, prototype) - { - my ($self, $type, $prototype) = @_; - - my $noParenthesisParameters = ($type eq ::TOPIC_FUNCTION() && $prototype =~ /^[^\(]*\@/); - - if ($prototype !~ /\(.*[^ ].*\)/ && !$noParenthesisParameters) - { return $self->SUPER::ParsePrototype($type, $prototype); }; - - - - my ($beforeParameters, $afterParameters, $isAfterParameters); - - if ($noParenthesisParameters) - { - ($beforeParameters, $prototype) = split(/\@/, $prototype, 2); - $prototype = '@' . $prototype; - }; - - my @tokens = $prototype =~ /([^\(\)\[\]\{\}\<\>\'\"\,]+|.)/g; - - my $parameter; - my @parameterLines; - - my @symbolStack; - - foreach my $token (@tokens) - { - if ($isAfterParameters) - { $afterParameters .= $token; } - - elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"') - { - if ($noParenthesisParameters || $symbolStack[0] eq '(') - { $parameter .= $token; } - else - { $beforeParameters .= $token; }; - - if ($token eq $symbolStack[-1]) - { pop @symbolStack; }; - } - - elsif ($token =~ /^[\(\[\{\<\'\"]$/) - { - if ($noParenthesisParameters || $symbolStack[0] eq '(') - { $parameter .= $token; } - else - { $beforeParameters .= $token; }; - - push @symbolStack, $token; - } - - elsif ( ($token eq ')' && $symbolStack[-1] eq '(') || - ($token eq ']' && $symbolStack[-1] eq '[') || - ($token eq '}' && $symbolStack[-1] eq '{') || - ($token eq '>' && $symbolStack[-1] eq '<') ) - { - if (!$noParenthesisParameters && $token eq ')' && scalar @symbolStack == 1 && $symbolStack[0] eq '(') - { - $afterParameters .= $token; - $isAfterParameters = 1; - } - else - { $parameter .= $token; }; - - pop @symbolStack; - } - - elsif ($token eq ',') - { - if (!scalar @symbolStack) - { - if ($noParenthesisParameters) - { - push @parameterLines, $parameter . $token; - $parameter = undef; - } - else - { - $beforeParameters .= $token; - }; - } - else - { - if (scalar @symbolStack == 1 && $symbolStack[0] eq '(' && !$noParenthesisParameters) - { - push @parameterLines, $parameter . $token; - $parameter = undef; - } - else - { - $parameter .= $token; - }; - }; - } - - else - { - if ($noParenthesisParameters || $symbolStack[0] eq '(') - { $parameter .= $token; } - else - { $beforeParameters .= $token; }; - }; - }; - - push @parameterLines, $parameter; - - foreach my $item (\$beforeParameters, \$afterParameters) - { - $$item =~ s/^ //; - $$item =~ s/ $//; - } - - my $prototypeObject = NaturalDocs::Languages::Prototype->New($beforeParameters, $afterParameters); - - - # Parse the actual parameters. - - foreach my $parameterLine (@parameterLines) - { - $prototypeObject->AddParameter( $self->ParseParameterLine($parameterLine) ); - }; - - return $prototypeObject; - }; - - -# -# Function: ParseParameterLine -# -# Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object. -# -sub ParseParameterLine #(line) - { - my ($self, $line) = @_; - - $line =~ s/^ //; - $line =~ s/ $//; - - my @tokens = $line =~ /([^\(\)\[\]\{\}\<\>\'\"\:\=\ ]+|\:\=|.)/g; - - my ($name, $type, $defaultValue, $defaultValuePrefix, $inType, $inDefaultValue); - - - my @symbolStack; - - foreach my $token (@tokens) - { - if ($inDefaultValue) - { $defaultValue .= $token; } - - elsif ($symbolStack[-1] eq '\'' || $symbolStack[-1] eq '"') - { - if ($inType) - { $type .= $token; } - else - { $name .= $token; }; - - if ($token eq $symbolStack[-1]) - { pop @symbolStack; }; - } - - elsif ($token =~ /^[\(\[\{\<\'\"]$/) - { - if ($inType) - { $type .= $token; } - else - { $name .= $token; }; - - push @symbolStack, $token; - } - - elsif ( ($token eq ')' && $symbolStack[-1] eq '(') || - ($token eq ']' && $symbolStack[-1] eq '[') || - ($token eq '}' && $symbolStack[-1] eq '{') || - ($token eq '>' && $symbolStack[-1] eq '<') ) - { - if ($inType) - { $type .= $token; } - else - { $name .= $token; }; - - pop @symbolStack; - } - - elsif ($token eq ' ') - { - if ($inType) - { $type .= $token; } - elsif (!scalar @symbolStack) - { $inType = 1; } - else - { $name .= $token; }; - } - - elsif ($token eq ':=' || $token eq '=') - { - if (!scalar @symbolStack) - { - $defaultValuePrefix = $token; - $inDefaultValue = 1; - } - elsif ($inType) - { $type .= $token; } - else - { $name .= $token; }; - } - - else - { - if ($inType) - { $type .= $token; } - else - { $name .= $token; }; - }; - }; - - foreach my $part (\$type, \$defaultValue) - { - $$part =~ s/ $//; - }; - - return NaturalDocs::Languages::Prototype::Parameter->New($type, undef, $name, undef, $defaultValue, $defaultValuePrefix); - }; - - -sub TypeBeforeParameter - { return 0; }; - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Pascal.pm b/docs/doctool/Modules/NaturalDocs/Languages/Pascal.pm deleted file mode 100644 index b6c4a018..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Pascal.pm +++ /dev/null @@ -1,143 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Pascal -# -############################################################################### -# -# A subclass to handle the language variations of Pascal and Delphi. -# -############################################################################### - -# 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::Languages::Pascal; - -use base 'NaturalDocs::Languages::Simple'; - - -# -# hash: prototypeDirectives -# -# An existence hash of all the directives that can appear after a function prototype and will be included. The keys are the all -# lowercase keywords. -# -my %prototypeDirectives = ( 'overload' => 1, - 'override' => 1, - 'virtual' => 1, - 'abstract' => 1, - 'reintroduce' => 1, - 'export' => 1, - 'public' => 1, - 'interrupt' => 1, - 'register' => 1, - 'pascal' => 1, - 'cdecl' => 1, - 'stdcall' => 1, - 'popstack' => 1, - 'saveregisters' => 1, - 'inline' => 1, - 'safecall' => 1 ); - -# -# hash: longPrototypeDirectives -# -# An existence hash of all the directives with parameters that can appear after a function prototype and will be included. The -# keys are the all lowercase keywords. -# -my %longPrototypeDirectives = ( 'alias' => 1, - 'external' => 1 ); - -# -# bool: checkingForDirectives -# -# Set after the first function semicolon, which means we're in directives mode. -# -my $checkingForDirectives; - - -# -# Function: OnCode -# -# Just overridden to reset <checkingForDirectives>. -# -sub OnCode #(...) - { - my ($self, @parameters) = @_; - - $checkingForDirectives = 0; - - return $self->SUPER::OnCode(@parameters); - }; - - -# -# Function: OnPrototypeEnd -# -# Pascal's syntax has directives after the prototype that should be included. -# -# > function MyFunction ( param1: type ); virtual; abstract; -# -# Parameters: -# -# type - The <TopicType> of the prototype. -# prototypeRef - A reference to the prototype so far, minus the ender in dispute. -# ender - The ender symbol. -# -# Returns: -# -# ENDER_ACCEPT - The ender is accepted and the prototype is finished. -# ENDER_IGNORE - The ender is rejected and parsing should continue. Note that the prototype will be rejected as a whole -# if all enders are ignored before reaching the end of the code. -# ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is. However, the prototype might -# also continue on so continue parsing. If there is no accepted ender between here and -# the end of the code this version will be accepted instead. -# ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed. Use the last accepted -# version and end parsing. -# -sub OnPrototypeEnd #(type, prototypeRef, ender) - { - my ($self, $type, $prototypeRef, $ender) = @_; - - if ($type eq ::TOPIC_FUNCTION() && $ender eq ';') - { - if (!$checkingForDirectives) - { - $checkingForDirectives = 1; - return ::ENDER_ACCEPT_AND_CONTINUE(); - } - elsif ($$prototypeRef =~ /;[ \t]*([a-z]+)([^;]*)$/i) - { - my ($lastDirective, $extra) = (lc($1), $2); - - if (exists $prototypeDirectives{$lastDirective} && $extra =~ /^[ \t]*$/) - { return ::ENDER_ACCEPT_AND_CONTINUE(); } - elsif (exists $longPrototypeDirectives{$lastDirective}) - { return ::ENDER_ACCEPT_AND_CONTINUE(); } - else - { return ::ENDER_REVERT_TO_ACCEPTED(); }; - } - else - { return ::ENDER_REVERT_TO_ACCEPTED(); }; - } - else - { return ::ENDER_ACCEPT(); }; - }; - - -sub ParseParameterLine #(...) - { - my ($self, @params) = @_; - return $self->SUPER::ParsePascalParameterLine(@params); - }; - -sub TypeBeforeParameter - { - return 0; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Perl.pm b/docs/doctool/Modules/NaturalDocs/Languages/Perl.pm deleted file mode 100644 index ca9d24fc..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Perl.pm +++ /dev/null @@ -1,1338 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Perl -# -############################################################################### -# -# A subclass to handle the language variations of Perl. -# -# -# Topic: Language Support -# -# Supported: -# -# - Packages -# - Inheritance via "use base" and "@ISA =". -# - Functions -# - Variables -# -# Not supported yet: -# -# - Constants -# -############################################################################### - -# 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::Languages::Perl; - -use base 'NaturalDocs::Languages::Advanced'; - - -# -# bool: inNDPOD -# Set whenever we're in ND POD in <PreprocessLine()>. -# -my $inNDPOD; - -# -# bool: mustBreakPOD -# Set whenever the next line needs to be prefixed with "(NDPODBREAK)" in <PreprocessLine()>. -# -my $mustBreakPOD; - -# -# array: hereDocTerminators -# An array of active Here Doc terminators, or an empty array if not active. Each entry is an arrayref of tokens. The entries -# must appear in the order they must appear in the source. -# -my @hereDocTerminators; - - - -############################################################################### -# Group: Interface Functions - - -# -# Function: PackageSeparator -# Returns the package separator symbol. -# -sub PackageSeparator - { return '::'; }; - -# -# Function: EnumValues -# Returns the <EnumValuesType> that describes how the language handles enums. -# -sub EnumValues - { return ::ENUM_GLOBAL(); }; - - -# -# Function: ParseFile -# -# Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()>. -# -# Parameters: -# -# sourceFile - The name of the source file to parse. -# topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file. -# -# Returns: -# -# The array ( autoTopics, scopeRecord ). -# -# autoTopics - An arrayref of automatically generated topics from the file, or undef if none. -# scopeRecord - An arrayref of <NaturalDocs::Languages::Advanced::ScopeChanges>, or undef if none. -# -sub ParseFile #(sourceFile, topicsList) - { - my ($self, $sourceFile, $topicsList) = @_; - - $inNDPOD = 0; - $mustBreakPOD = 0; - @hereDocTerminators = ( ); - - $self->ParseForCommentsAndTokens($sourceFile, [ '#' ], [ '=begin nd', '=end nd' ]); - - my $tokens = $self->Tokens(); - my $index = 0; - my $lineNumber = 1; - - while ($index < scalar @$tokens) - { - if ($self->TryToSkipWhitespace(\$index, \$lineNumber) || - $self->TryToGetPackage(\$index, \$lineNumber) || - $self->TryToGetBase(\$index, \$lineNumber) || - $self->TryToGetFunction(\$index, \$lineNumber) || - $self->TryToGetVariable(\$index, \$lineNumber) ) - { - # The functions above will handle everything. - } - - elsif ($tokens->[$index] eq '{') - { - $self->StartScope('}', $lineNumber, undef); - $index++; - } - - elsif ($tokens->[$index] eq '}') - { - if ($self->ClosingScopeSymbol() eq '}') - { $self->EndScope($lineNumber); }; - - $index++; - } - - elsif (lc($tokens->[$index]) eq 'eval') - { - # We want to skip the token in this case instead of letting it fall to SkipRestOfStatement. This allows evals with braces - # to be treated like normal floating braces. - $index++; - } - - else - { - $self->SkipRestOfStatement(\$index, \$lineNumber); - }; - }; - - - # Don't need to keep these around. - $self->ClearTokens(); - - return ( $self->AutoTopics(), $self->ScopeRecord() ); - }; - - -# -# Function: PreprocessLine -# -# Overridden to support "=begin nd" and similar. -# -# - "=begin [nd|naturaldocs|natural docs]" all translate to "=begin nd". -# - "=[nd|naturaldocs|natural docs]" also translate to "=begin nd". -# - "=end [nd|naturaldocs|natural docs]" all translate to "=end nd". -# - "=cut" from a ND block translates into "=end nd", but the next line will be altered to begin with "(NDPODBREAK)". This is -# so if there is POD leading into ND which ends with a cut, the parser can still end the original POD because the end ND line -# would have been removed. -# - "=pod begin nd" and "=pod end nd" are supported for compatibility with ND 1.32 and earlier, even though the syntax is a -# mistake. -# -sub PreprocessLine #(lineRef) - { - my ($self, $lineRef) = @_; - - if ($$lineRef =~ /^\=(?:(?:pod[ \t]+)?begin[ \t]+)?(?:nd|naturaldocs|natural[ \t]+docs)[ \t]*$/i) - { - $$lineRef = '=begin nd'; - $inNDPOD = 1; - $mustBreakPOD = 0; - } - elsif ($$lineRef =~ /^\=(?:pod[ \t]+)end[ \t]+(?:nd|naturaldocs|natural[ \t]+docs)[ \t]*$/i) - { - $$lineRef = '=end nd'; - $inNDPOD = 0; - $mustBreakPOD = 0; - } - elsif ($$lineRef =~ /^\=cut[ \t]*$/i) - { - if ($inNDPOD) - { - $$lineRef = '=end nd'; - $inNDPOD = 0; - $mustBreakPOD = 1; - }; - } - elsif ($mustBreakPOD) - { - $$lineRef = '(NDPODBREAK)' . $$lineRef; - $mustBreakPOD = 0; - }; - }; - - - -############################################################################### -# Group: Statement Parsing Functions -# All functions here assume that the current position is at the beginning of a statement. -# -# Note for developers: I am well aware that the code in these functions do not check if we're past the end of the tokens as -# often as it should. We're making use of the fact that Perl will always return undef in these cases to keep the code simpler. - - -# -# Function: TryToGetPackage -# -# Determines whether the position is at a package declaration statement, and if so, generates a topic for it, skips it, and -# returns true. -# -sub TryToGetPackage #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - if (lc($tokens->[$$indexRef]) eq 'package') - { - my $index = $$indexRef + 1; - my $lineNumber = $$lineNumberRef; - - if (!$self->TryToSkipWhitespace(\$index, \$lineNumber)) - { return undef; }; - - my $name; - - while ($tokens->[$index] =~ /^[a-z_\:]/i) - { - $name .= $tokens->[$index]; - $index++; - }; - - if (!defined $name) - { return undef; }; - - my $autoTopic = NaturalDocs::Parser::ParsedTopic->New(::TOPIC_CLASS(), $name, - undef, undef, - undef, - undef, undef, $$lineNumberRef); - $self->AddAutoTopic($autoTopic); - - NaturalDocs::Parser->OnClass($autoTopic->Symbol()); - - $self->SetPackage($autoTopic->Symbol(), $$lineNumberRef); - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - $self->SkipRestOfStatement($indexRef, $lineNumberRef); - - return 1; - }; - - return undef; - }; - - -# -# Function: TryToGetBase -# -# Determines whether the position is at a package base declaration statement, and if so, calls -# <NaturalDocs::Parser->OnClassParent()>. -# -# Supported Syntaxes: -# -# > use base [list of strings] -# > @ISA = [list of strings] -# > @[package]::ISA = [list of strings] -# > our @ISA = [list of strings] -# -sub TryToGetBase #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my ($index, $lineNumber, $class, $parents); - - if (lc($tokens->[$$indexRef]) eq 'use') - { - $index = $$indexRef + 1; - $lineNumber = $$lineNumberRef; - - if (!$self->TryToSkipWhitespace(\$index, \$lineNumber) || - lc($tokens->[$index]) ne 'base') - { return undef; } - - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - $parents = $self->TryToGetListOfStrings(\$index, \$lineNumber); - } - - else - { - $index = $$indexRef; - $lineNumber = $$lineNumberRef; - - if (lc($tokens->[$index]) eq 'our') - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - }; - - if ($tokens->[$index] eq '@') - { - $index++; - - while ($index < scalar @$tokens) - { - if ($tokens->[$index] eq 'ISA') - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - if ($tokens->[$index] eq '=') - { - $index++; - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - $parents = $self->TryToGetListOfStrings(\$index, \$lineNumber); - } - else - { last; }; - } - - # If token isn't ISA... - elsif ($tokens->[$index] =~ /^[a-z0-9_:]/i) - { - $class .= $tokens->[$index]; - $index++; - } - else - { last; }; - }; - }; - }; - - if (defined $parents) - { - if (defined $class) - { - $class =~ s/::$//; - my @classIdentifiers = split(/::/, $class); - $class = NaturalDocs::SymbolString->Join(@classIdentifiers); - } - else - { $class = $self->CurrentScope(); }; - - foreach my $parent (@$parents) - { - my @parentIdentifiers = split(/::/, $parent); - my $parentSymbol = NaturalDocs::SymbolString->Join(@parentIdentifiers); - - NaturalDocs::Parser->OnClassParent($class, $parentSymbol, undef, undef, ::RESOLVE_ABSOLUTE()); - }; - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - $self->SkipRestOfStatement($indexRef, $lineNumberRef); - - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToGetFunction -# -# Determines whether the position is at a function declaration statement, and if so, generates a topic for it, skips it, and -# returns true. -# -sub TryToGetFunction #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - if ( lc($tokens->[$$indexRef]) eq 'sub') - { - my $prototypeStart = $$indexRef; - my $prototypeStartLine = $$lineNumberRef; - my $prototypeEnd = $$indexRef + 1; - my $prototypeEndLine = $$lineNumberRef; - - if ( !$self->TryToSkipWhitespace(\$prototypeEnd, \$prototypeEndLine) || - $tokens->[$prototypeEnd] !~ /^[a-z_]/i ) - { return undef; }; - - my $name = $tokens->[$prototypeEnd]; - $prototypeEnd++; - - # We parsed 'sub [name]'. Now keep going until we find a semicolon or a brace. - - for (;;) - { - if ($prototypeEnd >= scalar @$tokens) - { return undef; } - - # End if we find a semicolon, since it means we found a predeclaration rather than an actual function. - elsif ($tokens->[$prototypeEnd] eq ';') - { return undef; } - - elsif ($tokens->[$prototypeEnd] eq '{') - { - # Found it! - - my $prototype = $self->NormalizePrototype( $self->CreateString($prototypeStart, $prototypeEnd) ); - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_FUNCTION(), $name, - $self->CurrentScope(), undef, - $prototype, - undef, undef, $prototypeStartLine)); - - $$indexRef = $prototypeEnd; - $$lineNumberRef = $prototypeEndLine; - - $self->SkipRestOfStatement($indexRef, $lineNumberRef); - - return 1; - } - - else - { $self->GenericSkip(\$prototypeEnd, \$prototypeEndLine, 0, 1); }; - }; - } - else - { return undef; }; - }; - - -# -# Function: TryToGetVariable -# -# Determines if the position is at a variable declaration statement, and if so, generates a topic for it, skips it, and returns -# true. -# -# Supported Syntaxes: -# -# - Supports variables declared with "my", "our", and "local". -# - Supports multiple declarations in one statement, such as "my ($x, $y);". -# - Supports types and attributes. -# -sub TryToGetVariable #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $firstToken = lc( $tokens->[$$indexRef] ); - - if ($firstToken eq 'my' || $firstToken eq 'our' || $firstToken eq 'local') - { - my $prototypeStart = $$indexRef; - my $prototypeStartLine = $$lineNumberRef; - my $prototypeEnd = $$indexRef + 1; - my $prototypeEndLine = $$lineNumberRef; - - $self->TryToSkipWhitespace(\$prototypeEnd, \$prototypeEndLine); - - - # Get the type if present. - - my $type; - - if ($tokens->[$prototypeEnd] =~ /^[a-z\:]/i) - { - do - { - $type .= $tokens->[$prototypeEnd]; - $prototypeEnd++; - } - while ($tokens->[$prototypeEnd] =~ /^[a-z\:]/i); - - if (!$self->TryToSkipWhitespace(\$prototypeEnd, \$prototypeEndLine)) - { return undef; }; - }; - - - # Get the name, or possibly names. - - if ($tokens->[$prototypeEnd] eq '(') - { - # If there's multiple variables, we'll need to build a custom prototype for each one. $firstToken already has the - # declaring word. We're going to store each name in @names, and we're going to use $prototypeStart and - # $prototypeEnd to capture any properties appearing after the list. - - my $name; - my @names; - my $hasComma = 0; - - $prototypeStart = $prototypeEnd + 1; - $prototypeStartLine = $prototypeEndLine; - - for (;;) - { - $self->TryToSkipWhitespace(\$prototypeStart, \$prototypeStartLine); - - $name = $self->TryToGetVariableName(\$prototypeStart, \$prototypeStartLine); - - if (!defined $name) - { return undef; }; - - push @names, $name; - - $self->TryToSkipWhitespace(\$prototypeStart, \$prototypeStartLine); - - # We can have multiple commas in a row. We can also have trailing commas. However, the parenthesis must - # not start with a comma or be empty, hence this logic does not appear earlier. - while ($tokens->[$prototypeStart] eq ',') - { - $prototypeStart++; - $self->TryToSkipWhitespace(\$prototypeStart, \$prototypeStartLine); - - $hasComma = 1; - } - - if ($tokens->[$prototypeStart] eq ')') - { - $prototypeStart++; - last; - } - elsif (!$hasComma) - { return undef; }; - }; - - - # Now find the end of the prototype. - - $prototypeEnd = $prototypeStart; - $prototypeEndLine = $prototypeStartLine; - - while ($prototypeEnd < scalar @$tokens && - $tokens->[$prototypeEnd] !~ /^[\;\=]/) - { - $prototypeEnd++; - }; - - - my $prototypePrefix = $firstToken . ' '; - if (defined $type) - { $prototypePrefix .= $type . ' '; }; - - my $prototypeSuffix = ' ' . $self->CreateString($prototypeStart, $prototypeEnd); - - foreach $name (@names) - { - my $prototype = $self->NormalizePrototype( $prototypePrefix . $name . $prototypeSuffix ); - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_VARIABLE(), $name, - $self->CurrentScope(), undef, - $prototype, - undef, undef, $prototypeStartLine)); - }; - - $self->SkipRestOfStatement(\$prototypeEnd, \$prototypeEndLine); - - $$indexRef = $prototypeEnd; - $$lineNumberRef = $prototypeEndLine; - } - - else # no parenthesis - { - my $name = $self->TryToGetVariableName(\$prototypeEnd, \$prototypeEndLine); - - if (!defined $name) - { return undef; }; - - while ($prototypeEnd < scalar @$tokens && - $tokens->[$prototypeEnd] !~ /^[\;\=]/) - { - $prototypeEnd++; - }; - - my $prototype = $self->NormalizePrototype( $self->CreateString($prototypeStart, $prototypeEnd) ); - - $self->AddAutoTopic(NaturalDocs::Parser::ParsedTopic->New(::TOPIC_VARIABLE(), $name, - $self->CurrentScope(), undef, - $prototype, - undef, undef, $prototypeStartLine)); - - $self->SkipRestOfStatement(\$prototypeEnd, \$prototypeEndLine); - - $$indexRef = $prototypeEnd; - $$lineNumberRef = $prototypeEndLine; - }; - - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToGetVariableName -# -# Determines if the position is at a variable name, and if so, skips it and returns the name. -# -sub TryToGetVariableName #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $name; - - if ($tokens->[$$indexRef] =~ /^[\$\@\%\*]/) - { - $name .= $tokens->[$$indexRef]; - $$indexRef++; - - $self->TryToSkipWhitespace($indexRef, $lineNumberRef); - - if ($tokens->[$$indexRef] =~ /^[a-z_]/i) - { - $name .= $tokens->[$$indexRef]; - $$indexRef++; - } - else - { return undef; }; - }; - - return $name; - }; - - -# -# Function: TryToGetListOfStrings -# -# Attempts to retrieve a list of strings from the current position. Returns an arrayref of them if any are found, or undef if none. -# It stops the moment it reaches a non-string, so "string1, variable, string2" will only return string1. -# -# Supported Syntaxes: -# -# - Supports parenthesis. -# - Supports all string forms supported by <TryToSkipString()>. -# - Supports qw() string arrays. -# -sub TryToGetListOfStrings #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $parenthesis = 0; - my $strings; - - while ($$indexRef < scalar @$tokens) - { - # We'll tolerate parenthesis. - if ($tokens->[$$indexRef] eq '(') - { - $$indexRef++; - $parenthesis++; - } - elsif ($tokens->[$$indexRef] eq ')') - { - if ($parenthesis == 0) - { last; }; - - $$indexRef++; - $parenthesis--; - } - elsif ($tokens->[$$indexRef] eq ',') - { - $$indexRef++; - } - else - { - my ($startContent, $endContent); - my $symbolIndex = $$indexRef; - - if ($self->TryToSkipString($indexRef, $lineNumberRef, \$startContent, \$endContent)) - { - my $content = $self->CreateString($startContent, $endContent); - - if (!defined $strings) - { $strings = [ ]; }; - - if (lc($tokens->[$symbolIndex]) eq 'qw') - { - $content =~ tr/ \t\n/ /s; - $content =~ s/^ //; - - my @qwStrings = split(/ /, $content); - - push @$strings, @qwStrings; - } - else - { - push @$strings, $content; - }; - } - else - { last; }; - }; - - $self->TryToSkipWhitespace($indexRef, $lineNumberRef); - }; - - return $strings; - }; - - -############################################################################### -# Group: Low Level Parsing Functions - - -# -# Function: GenericSkip -# -# Advances the position one place through general code. -# -# - If the position is on a comment or string, it will skip it completely. -# - If the position is on an opening symbol, it will skip until the past the closing symbol. -# - If the position is on a regexp or quote-like operator, it will skip it completely. -# - If the position is on a backslash, it will skip it and the following token. -# - If the position is on whitespace (including comments), it will skip it completely. -# - Otherwise it skips one token. -# -# Parameters: -# -# indexRef - A reference to the current index. -# lineNumberRef - A reference to the current line number. -# noRegExps - If set, does not test for regular expressions. -# allowStringedClosingParens - If set, allows $) to end a parenthesis set. -# -sub GenericSkip #(indexRef, lineNumberRef, noRegExps, allowStringedClosingParens) - { - my ($self, $indexRef, $lineNumberRef, $noRegExps, $allowStringedClosingParens) = @_; - my $tokens = $self->Tokens(); - - if ($tokens->[$$indexRef] eq "\\" && $$indexRef + 1 < scalar @$tokens && $tokens->[$$indexRef+1] ne "\n") - { $$indexRef += 2; } - - # Note that we don't want to count backslashed ()[]{} since they could be in regexps. Also, ()[] are valid variable names - # when preceded by a string. - - # We can ignore the scope stack because we're just skipping everything without parsing, and we need recursion anyway. - elsif ($tokens->[$$indexRef] eq '{' && !$self->IsBackslashed($$indexRef)) - { - $$indexRef++; - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, '}', $noRegExps, $allowStringedClosingParens); - } - elsif ($tokens->[$$indexRef] eq '(' && !$self->IsBackslashed($$indexRef) && !$self->IsStringed($$indexRef)) - { - $$indexRef++; - - do - { $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ')', $noRegExps, $allowStringedClosingParens); } - while ($$indexRef < scalar @$tokens && $self->IsStringed($$indexRef - 1) && !$allowStringedClosingParens); - } - elsif ($tokens->[$$indexRef] eq '[' && !$self->IsBackslashed($$indexRef) && !$self->IsStringed($$indexRef)) - { - $$indexRef++; - - do - { $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, ']', $noRegExps, $allowStringedClosingParens); } - while ($$indexRef < scalar @$tokens && $self->IsStringed($$indexRef - 1)); - } - - elsif ($self->TryToSkipWhitespace($indexRef, $lineNumberRef) || - $self->TryToSkipString($indexRef, $lineNumberRef) || - $self->TryToSkipHereDocDeclaration($indexRef, $lineNumberRef) || - (!$noRegExps && $self->TryToSkipRegexp($indexRef, $lineNumberRef) ) ) - { - } - - else - { $$indexRef++; }; - }; - - -# -# Function: GenericSkipUntilAfter -# -# Advances the position via <GenericSkip()> until a specific token is reached and passed. -# -sub GenericSkipUntilAfter #(indexRef, lineNumberRef, token, noRegExps, allowStringedClosingParens) - { - my ($self, $indexRef, $lineNumberRef, $token, $noRegExps, $allowStringedClosingParens) = @_; - my $tokens = $self->Tokens(); - - while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne $token) - { $self->GenericSkip($indexRef, $lineNumberRef, $noRegExps, $allowStringedClosingParens); }; - - if ($tokens->[$$indexRef] eq "\n") - { $$lineNumberRef++; }; - $$indexRef++; - }; - - -# -# Function: GenericRegexpSkip -# -# Advances the position one place through regexp code. -# -# - If the position is on an opening symbol, it will skip until the past the closing symbol. -# - If the position is on a backslash, it will skip it and the following token. -# - If the position is on whitespace (not including comments), it will skip it completely. -# - Otherwise it skips one token. -# -# Also differs from <GenericSkip()> in that the parenthesis in $( and $) do count against the scope, where they wouldn't -# normally. -# -# Parameters: -# -# indexRef - A reference to the current index. -# lineNumberRef - A reference to the current line number. -# inBrackets - Whether we're in brackets or not. If true, we don't care about matching braces and parenthesis. -# -sub GenericRegexpSkip #(indexRef, lineNumberRef, inBrackets) - { - my ($self, $indexRef, $lineNumberRef, $inBrackets) = @_; - my $tokens = $self->Tokens(); - - if ($tokens->[$$indexRef] eq "\\" && $$indexRef + 1 < scalar @$tokens && $tokens->[$$indexRef+1] ne "\n") - { $$indexRef += 2; } - - # We can ignore the scope stack because we're just skipping everything without parsing, and we need recursion anyway. - elsif ($tokens->[$$indexRef] eq '{' && !$self->IsBackslashed($$indexRef) && !$inBrackets) - { - $$indexRef++; - $self->GenericRegexpSkipUntilAfter($indexRef, $lineNumberRef, '}'); - } - elsif ($tokens->[$$indexRef] eq '(' && !$self->IsBackslashed($$indexRef) && !$inBrackets) - { - $$indexRef++; - $self->GenericRegexpSkipUntilAfter($indexRef, $lineNumberRef, ')'); - } - elsif ($tokens->[$$indexRef] eq '[' && !$self->IsBackslashed($$indexRef) && !$self->IsStringed($$indexRef)) - { - $$indexRef++; - - do - { $self->GenericRegexpSkipUntilAfter($indexRef, $lineNumberRef, ']'); } - while ($$indexRef < scalar @$tokens && $self->IsStringed($$indexRef - 1)); - } - - elsif ($tokens->[$$indexRef] eq "\n") - { - $$lineNumberRef++; - $$indexRef++; - } - - else - { $$indexRef++; }; - }; - - -# -# Function: GenericRegexpSkipUntilAfter -# -# Advances the position via <GenericRegexpSkip()> until a specific token is reached and passed. -# -sub GenericRegexpSkipUntilAfter #(indexRef, lineNumberRef, token) - { - my ($self, $indexRef, $lineNumberRef, $token) = @_; - my $tokens = $self->Tokens(); - - my $inBrackets = ( $token eq ']' ); - - while ($$indexRef < scalar @$tokens && $tokens->[$$indexRef] ne $token) - { $self->GenericRegexpSkip($indexRef, $lineNumberRef, $inBrackets); }; - - if ($tokens->[$$indexRef] eq "\n") - { $$lineNumberRef++; }; - $$indexRef++; - }; - - -# -# Function: SkipRestOfStatement -# -# Advances the position via <GenericSkip()> until after the end of the current statement, which is defined as a semicolon or -# a brace group. Of course, either of those appearing inside parenthesis, a nested brace group, etc. don't count. -# -sub SkipRestOfStatement #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - while ($$indexRef < scalar @$tokens && - $tokens->[$$indexRef] ne ';' && - !($tokens->[$$indexRef] eq '{' && !$self->IsStringed($$indexRef)) ) - { - $self->GenericSkip($indexRef, $lineNumberRef); - }; - - if ($tokens->[$$indexRef] eq ';') - { $$indexRef++; } - elsif ($tokens->[$$indexRef] eq '{') - { $self->GenericSkip($indexRef, $lineNumberRef); }; - }; - - -# -# Function: TryToSkipWhitespace -# -# If the current position is on whitespace it skips them and returns true. If there are a number of these in a row, it skips them -# all. -# -# Supported Syntax: -# -# - Whitespace -# - Line break -# - All comment forms supported by <TryToSkipComment()> -# - Here Doc content -# -sub TryToSkipWhitespace #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $result; - - while ($$indexRef < scalar @$tokens) - { - if ($self->TryToSkipHereDocContent($indexRef, $lineNumberRef) || - $self->TryToSkipComment($indexRef, $lineNumberRef)) - { - $result = 1; - } - elsif ($tokens->[$$indexRef] =~ /^[ \t]/) - { - $$indexRef++; - $result = 1; - } - elsif ($tokens->[$$indexRef] eq "\n") - { - $$indexRef++; - $$lineNumberRef++; - $result = 1; - } - else - { last; }; - }; - - return $result; - }; - - -# -# Function: TryToSkipComment -# If the current position is on a comment, skip past it and return true. -# -sub TryToSkipComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - - return ( $self->TryToSkipLineComment($indexRef, $lineNumberRef) || - $self->TryToSkipPODComment($indexRef, $lineNumberRef) ); - }; - - -# -# Function: TryToSkipLineComment -# If the current position is on a line comment symbol, skip past it and return true. -# -sub TryToSkipLineComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - # Note that $#var is not a comment. - if ($tokens->[$$indexRef] eq '#' && !$self->IsStringed($$indexRef)) - { - $self->SkipRestOfLine($indexRef, $lineNumberRef); - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToSkipPODComment -# If the current position is on a POD comment symbol, skip past it and return true. -# -sub TryToSkipPODComment #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - # Note that whitespace is not allowed before the equals sign. It must directly start a line. - if ($tokens->[$$indexRef] eq '=' && - ( $$indexRef == 0 || $tokens->[$$indexRef - 1] eq "\n" ) && - $tokens->[$$indexRef + 1] =~ /^[a-z]/i ) - { - # Skip until =cut. Note that it's theoretically possible for =cut to appear without a prior POD directive. - - do - { - if ($tokens->[$$indexRef] eq '=' && lc( $tokens->[$$indexRef + 1] ) eq 'cut') - { - $self->SkipRestOfLine($indexRef, $lineNumberRef); - last; - } - elsif ($tokens->[$$indexRef] eq '(' && $$indexRef + 2 < scalar @$tokens && - $tokens->[$$indexRef+1] eq 'NDPODBREAK' && $tokens->[$$indexRef+2] eq ')') - { - $$indexRef += 3; - last; - } - else - { - $self->SkipRestOfLine($indexRef, $lineNumberRef); - }; - } - while ($$indexRef < scalar @$tokens); - - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToSkipString -# If the current position is on a string delimiter, skip past the string and return true. -# -# Parameters: -# -# indexRef - A reference to the index of the position to start at. -# lineNumberRef - A reference to the line number of the position. -# startContentIndexRef - A reference to the variable in which to store the index of the first content token. May be undef. -# endContentIndexRef - A reference to the variable in which to store the index of the end of the content, which is one past -# the last content token. may be undef. -# -# Returns: -# -# Whether the position was at a string. The index, line number, and content index variabls will only be changed if true. -# -# Syntax Support: -# -# - Supports quotes, apostrophes, backticks, q(), qq(), qx(), and qw(). -# - All symbols are supported for the letter forms. -# -sub TryToSkipString #(indexRef, lineNumberRef, startContentIndexRef, endContentIndexRef) - { - my ($self, $indexRef, $lineNumberRef, $startContentIndexRef, $endContentIndexRef) = @_; - my $tokens = $self->Tokens(); - - # The three string delimiters. All three are Perl variables when preceded by a dollar sign. - if (!$self->IsStringed($$indexRef) && - ( $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '\'', '\'', $startContentIndexRef, $endContentIndexRef) || - $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '"', '"', $startContentIndexRef, $endContentIndexRef) || - $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, '`', '`', $startContentIndexRef, $endContentIndexRef) ) ) - { - return 1; - } - elsif ($tokens->[$$indexRef] =~ /^(?:q|qq|qx|qw)$/i && - ($$indexRef == 0 || $tokens->[$$indexRef - 1] !~ /^[\$\%\@\*]$/)) - { - $$indexRef++; - - $self->TryToSkipWhitespace($indexRef, $lineNumberRef); - - my $openingSymbol = $tokens->[$$indexRef]; - my $closingSymbol; - - if ($openingSymbol eq '{') - { $closingSymbol = '}'; } - elsif ($openingSymbol eq '(') - { $closingSymbol = ')'; } - elsif ($openingSymbol eq '[') - { $closingSymbol = ']'; } - elsif ($openingSymbol eq '<') - { $closingSymbol = '>'; } - else - { $closingSymbol = $openingSymbol; }; - - $self->SUPER::TryToSkipString($indexRef, $lineNumberRef, $openingSymbol, $closingSymbol, - $startContentIndexRef, $endContentIndexRef); - - return 1; - } - else - { return undef; }; - }; - - -# -# Function: TryToSkipHereDocDeclaration -# -# If the current position is on a Here Doc declaration, add its terminators to <hereDocTerminators> and skip it. -# -# Syntax Support: -# -# - Supports <<EOF -# - Supports << "String" with all string forms supported by <TryToSkipString()>. -# -sub TryToSkipHereDocDeclaration #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - if ($tokens->[$index] eq '<' && $tokens->[$index + 1] eq '<') - { - $index += 2; - my $success; - - # No whitespace allowed with the bare word. - if ($tokens->[$index] eq 'EOF') - { - push @hereDocTerminators, [ 'EOF' ]; - $index++; - $success = 1; - } - else - { - $self->TryToSkipWhitespace(\$index, \$lineNumber); - - my ($contentStart, $contentEnd); - if ($self->TryToSkipString(\$index, \$lineNumber, \$contentStart, \$contentEnd)) - { - push @hereDocTerminators, [ @{$tokens}[$contentStart..$contentEnd - 1] ]; - $success = 1; - }; - }; - - if ($success) - { - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - return 1; - }; - }; - - return 0; - }; - - -# -# Function: TryToSkipHereDocContent -# -# If the current position is at the beginning of a line and there are entries in <hereDocTerminators>, skips lines until all the -# terminators are exhausted or we reach the end of the file. -# -# Returns: -# -# Whether the position was on Here Doc content. -# -sub TryToSkipHereDocContent #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - # We don't use IsFirstLineToken() because it really needs to be the first line token. Whitespace is not allowed. - if ($$indexRef > 0 && $tokens->[$$indexRef - 1] eq "\n") - { - my $success = (scalar @hereDocTerminators > 0); - - while (scalar @hereDocTerminators && $$indexRef < scalar @$tokens) - { - my $terminatorIndex = 0; - - while ($hereDocTerminators[0]->[$terminatorIndex] eq $tokens->[$$indexRef]) - { - $terminatorIndex++; - $$indexRef++; - }; - - if ($terminatorIndex == scalar @{$hereDocTerminators[0]} && - ($tokens->[$$indexRef] eq "\n" || ($tokens->[$$indexRef] =~ /^[ \t]/ && $tokens->[$$indexRef + 1] eq "\n")) ) - { - shift @hereDocTerminators; - $$indexRef++; - $$lineNumberRef++; - } - else - { $self->SkipRestOfLine($indexRef, $lineNumberRef); }; - }; - - return $success; - } - - else - { return 0; }; - }; - - -# -# Function: TryToSkipRegexp -# If the current position is on a regular expression or a quote-like operator, skip past it and return true. -# -# Syntax Support: -# -# - Supports //, ??, m//, qr//, s///, tr///, and y///. -# - All symbols are supported for the letter forms. -# -sub TryToSkipRegexp #(indexRef, lineNumberRef) - { - my ($self, $indexRef, $lineNumberRef) = @_; - my $tokens = $self->Tokens(); - - my $isRegexp; - - if ($tokens->[$$indexRef] =~ /^(?:m|qr|s|tr|y|)$/i && - ($$indexRef == 0 || $tokens->[$$indexRef - 1] !~ /^[\$\%\@\*\-]$/) ) - { $isRegexp = 1; } - elsif ( ($tokens->[$$indexRef] eq '/' || $tokens->[$$indexRef] eq '?') && !$self->IsStringed($$indexRef) ) - { - my $index = $$indexRef - 1; - - while ($index >= 0 && $tokens->[$index] =~ /^(?: |\t|\n)/) - { $index--; }; - - if ($index < 0 || $tokens->[$index] !~ /^[a-zA-Z0-9_\)\]\}\'\"\`]/) - { $isRegexp = 1; }; - }; - - if ($isRegexp) - { - my $operator = lc($tokens->[$$indexRef]); - my $index = $$indexRef; - my $lineNumber = $$lineNumberRef; - - if ($operator =~ /^[\?\/]/) - { $operator = 'm'; } - else - { - $index++; - - # Believe it or not, s#...# is allowed. We can't pass over number signs here. - if ($tokens->[$index] ne '#') - { $self->TryToSkipWhitespace(\$index, \$lineNumber); }; - }; - - if ($tokens->[$index] =~ /^\w/) - { return undef; }; - - my $openingSymbol = $tokens->[$index]; - my $closingSymbol; - - if ($openingSymbol eq '{') - { $closingSymbol = '}'; } - elsif ($openingSymbol eq '(') - { $closingSymbol = ')'; } - elsif ($openingSymbol eq '[') - { $closingSymbol = ']'; } - elsif ($openingSymbol eq '<') - { $closingSymbol = '>'; } - else - { $closingSymbol = $openingSymbol; }; - - $index++; - - $self->GenericRegexpSkipUntilAfter(\$index, \$lineNumber, $closingSymbol); - - $$indexRef = $index; - $$lineNumberRef = $lineNumber; - - if ($operator =~ /^(?:s|tr|y)$/) - { - if ($openingSymbol ne $closingSymbol) - { - $self->TryToSkipWhitespace($indexRef, $lineNumberRef); - - $openingSymbol = $tokens->[$index]; - - if ($openingSymbol eq '{') - { $closingSymbol = '}'; } - elsif ($openingSymbol eq '(') - { $closingSymbol = ')'; } - elsif ($openingSymbol eq '[') - { $closingSymbol = ']'; } - elsif ($openingSymbol eq '<') - { $closingSymbol = '>'; } - else - { $closingSymbol = $openingSymbol; }; - - $$indexRef++; - }; - - if ($operator eq 's') - { - $self->GenericSkipUntilAfter($indexRef, $lineNumberRef, $closingSymbol, 1); - } - else # ($operator eq 'tr' || $operator eq 'y') - { - while ($$indexRef < scalar @$tokens && - ($tokens->[$$indexRef] ne $closingSymbol || $self->IsBackslashed($$indexRef)) ) - { - if ($tokens->[$$indexRef] eq "\n") - { $$lineNumberRef++; }; - $$indexRef++; - }; - - $$indexRef++; - }; - }; - - # We want to skip any letters after the regexp. Otherwise something like tr/a/b/s; could have the trailing s; interpreted - # as another regexp. Whitespace is not allowed between the closing symbol and the letters. - - if ($tokens->[$$indexRef] =~ /^[a-z]/i) - { $$indexRef++; }; - - return 1; - }; - - return undef; - }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: IsStringed -# -# Returns whether the position is after a string (dollar sign) character. -# -# Parameters: -# -# index - The index of the postition. -# -sub IsStringed #(index) - { - my ($self, $index) = @_; - my $tokens = $self->Tokens(); - - if ($index > 0 && $tokens->[$index - 1] eq '$') - { return 1; } - else - { return undef; }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Prototype.pm b/docs/doctool/Modules/NaturalDocs/Languages/Prototype.pm deleted file mode 100644 index e529b89a..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Prototype.pm +++ /dev/null @@ -1,92 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Prototype -# -############################################################################### -# -# A data class for storing parsed prototypes. -# -############################################################################### - -# 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; - -use NaturalDocs::Languages::Prototype::Parameter; - - -package NaturalDocs::Languages::Prototype; - -use NaturalDocs::DefineMembers 'BEFORE_PARAMETERS', 'BeforeParameters()', 'SetBeforeParameters()', - 'AFTER_PARAMETERS', 'AfterParameters()', 'SetAfterParameters()', - 'PARAMETERS', 'Parameters()'; -# Dependency: New(), constant order, no parents. - - -# -# Function: New -# -# Creates and returns a new prototype object. -# -# Parameters: -# -# beforeParameters - The part of the prototype before the parameter list. -# afterParameters - The part of the prototype after the parameter list. -# -# You cannot set the parameters from here. Use <AddParameter()>. -# -sub New #(beforeParameters, afterParameters) - { - my ($package, @params) = @_; - - # Dependency: Constant order, no parents. - - my $object = [ @params ]; - bless $object, $package; - - return $object; - }; - - -# -# Functions: Members -# -# BeforeParameters - Returns the part of the prototype before the parameter list. If there is no parameter list, this will be the -# only thing that returns content. -# SetBeforeParameters - Replaces the part of the prototype before the parameter list. -# AfterParameters - Returns the part of the prototype after the parameter list, if any. -# SetAfterParameters - Replaces the part of the prototype after the parameter list. -# Parameters - Returns the parameter list as an arrayref of <NaturalDocs::Languages::Prototype::Parameters>, or undef if none. -# - -# -# Function: AddParameter -# -# Adds a <NaturalDocs::Languages::Prototype::Parameter> to the list. -# -sub AddParameter #(parameter) - { - my ($self, $parameter) = @_; - - if (!defined $self->[PARAMETERS]) - { $self->[PARAMETERS] = [ ]; }; - - push @{$self->[PARAMETERS]}, $parameter; - }; - - -# -# Function: OnlyBeforeParameters -# -# Returns whether <BeforeParameters()> is the only thing set. -# -sub OnlyBeforeParameters - { - my $self = shift; - return (!defined $self->[PARAMETERS] && !defined $self->[AFTER_PARAMETERS]); - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Prototype/Parameter.pm b/docs/doctool/Modules/NaturalDocs/Languages/Prototype/Parameter.pm deleted file mode 100644 index f1f65b08..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Prototype/Parameter.pm +++ /dev/null @@ -1,74 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Prototype::Parameter -# -############################################################################### -# -# A data class for storing parsed prototype parameters. -# -############################################################################### - -# 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::Languages::Prototype::Parameter; - -use NaturalDocs::DefineMembers 'TYPE', 'Type()', 'SetType()', - 'TYPE_PREFIX', 'TypePrefix()', 'SetTypePrefix()', - 'NAME', 'Name()', 'SetName()', - 'NAME_PREFIX', 'NamePrefix()', 'SetNamePrefix()', - 'DEFAULT_VALUE', 'DefaultValue()', 'SetDefaultValue()', - 'DEFAULT_VALUE_PREFIX', 'DefaultValuePrefix()', 'SetDefaultValuePrefix()'; -# Dependency: New() depends on the order of these constants and that they don't inherit from another class. - - -# -# Function: New -# -# Creates and returns a new prototype object. -# -# Parameters: -# -# type - The parameter type, if any. -# typePrefix - The parameter type prefix which should be aligned separately, if any. -# name - The parameter name. -# namePrefix - The parameter name prefix which should be aligned separately, if any. -# defaultValue - The default value expression, if any. -# defaultValuePrefix - The default value prefix which should be aligned separately, if any. -# -sub New #(type, typePrefix, name, namePrefix, defaultValue, defaultValuePrefix) - { - my ($package, @params) = @_; - - # Dependency: This depends on the order of the parameters being the same as the order of the constants, and that the - # constants don't inherit from another class. - - my $object = [ @params ]; - bless $object, $package; - - return $object; - }; - - -# -# Functions: Members -# -# Type - The parameter type, if any. -# SetType - Replaces the parameter type. -# TypePrefix - The parameter type prefix, which should be aligned separately, if any. -# SetTypePrefix - Replaces the parameter type prefix. -# Name - The parameter name. -# SetName - Replaces the parameter name. -# NamePrefix - The parameter name prefix, which should be aligned separately, if any. -# SetNamePrefix - Replaces the parameter name prefix. -# DefaultValue - The default value expression, if any. -# SetDefaultValue - Replaces the default value expression. -# DefaultValuePrefix - The default value prefix, which should be aligned separately, if any. -# SetDefaultValuePrefix - Replaces the default value prefix. -# - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Simple.pm b/docs/doctool/Modules/NaturalDocs/Languages/Simple.pm deleted file mode 100644 index 8e5762be..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Simple.pm +++ /dev/null @@ -1,495 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Simple -# -############################################################################### -# -# A class containing the characteristics of a particular programming language for basic support within Natural Docs. -# Also serves as a base class for languages that break from general conventions, such as not having parameter lists use -# parenthesis and commas. -# -############################################################################### - -# 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::Languages::Simple; - -use base 'NaturalDocs::Languages::Base'; -use base 'Exporter'; - -our @EXPORT = ( 'ENDER_ACCEPT', 'ENDER_IGNORE', 'ENDER_ACCEPT_AND_CONTINUE', 'ENDER_REVERT_TO_ACCEPTED' ); - - -use NaturalDocs::DefineMembers 'LINE_COMMENT_SYMBOLS', 'LineCommentSymbols()', 'SetLineCommentSymbols() duparrayref', - 'BLOCK_COMMENT_SYMBOLS', 'BlockCommentSymbols()', - 'SetBlockCommentSymbols() duparrayref', - 'PROTOTYPE_ENDERS', - 'LINE_EXTENDER', 'LineExtender()', 'SetLineExtender()', - 'PACKAGE_SEPARATOR', 'PackageSeparator()', - 'PACKAGE_SEPARATOR_WAS_SET', 'PackageSeparatorWasSet()', - 'ENUM_VALUES', 'EnumValues()', - 'ENUM_VALUES_WAS_SET', 'EnumValuesWasSet()'; - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# name - The name of the language. -# -sub New #(name) - { - my ($selfPackage, $name) = @_; - - my $object = $selfPackage->SUPER::New($name); - - $object->[ENUM_VALUES] = ::ENUM_GLOBAL(); - $object->[PACKAGE_SEPARATOR] = '.'; - - return $object; - }; - - -# -# Functions: Members -# -# LineCommentSymbols - Returns an arrayref of symbols that start a line comment, or undef if none. -# SetLineCommentSymbols - Replaces the arrayref of symbols that start a line comment. -# BlockCommentSymbols - Returns an arrayref of start/end symbol pairs that specify a block comment, or undef if none. Pairs -# are specified with two consecutive array entries. -# SetBlockCommentSymbols - Replaces the arrayref of start/end symbol pairs that specify a block comment. Pairs are -# specified with two consecutive array entries. -# LineExtender - Returns the symbol to ignore a line break in languages where line breaks are significant. -# SetLineExtender - Replaces the symbol to ignore a line break in languages where line breaks are significant. -# PackageSeparator - Returns the package separator symbol. -# PackageSeparatorWasSet - Returns whether the package separator symbol was ever changed from the default. -# - -# -# Function: SetPackageSeparator -# Replaces the language's package separator string. -# -sub SetPackageSeparator #(separator) - { - my ($self, $separator) = @_; - $self->[PACKAGE_SEPARATOR] = $separator; - $self->[PACKAGE_SEPARATOR_WAS_SET] = 1; - }; - - -# -# Functions: Members -# -# EnumValues - Returns the <EnumValuesType> that describes how the language handles enums. -# EnumValuesWasSet - Returns whether <EnumValues> was ever changed from the default. - - -# -# Function: SetEnumValues -# Replaces the <EnumValuesType> that describes how the language handles enums. -# -sub SetEnumValues #(EnumValuesType newBehavior) - { - my ($self, $behavior) = @_; - $self->[ENUM_VALUES] = $behavior; - $self->[ENUM_VALUES_WAS_SET] = 1; - }; - - -# -# Function: PrototypeEndersFor -# -# Returns an arrayref of prototype ender symbols for the passed <TopicType>, or undef if none. -# -sub PrototypeEndersFor #(type) - { - my ($self, $type) = @_; - - if (defined $self->[PROTOTYPE_ENDERS]) - { return $self->[PROTOTYPE_ENDERS]->{$type}; } - else - { return undef; }; - }; - - -# -# Function: SetPrototypeEndersFor -# -# Replaces the arrayref of prototype ender symbols for the passed <TopicType>. -# -sub SetPrototypeEndersFor #(type, enders) - { - my ($self, $type, $enders) = @_; - - if (!defined $self->[PROTOTYPE_ENDERS]) - { $self->[PROTOTYPE_ENDERS] = { }; }; - - if (!defined $enders) - { delete $self->[PROTOTYPE_ENDERS]->{$type}; } - else - { - $self->[PROTOTYPE_ENDERS]->{$type} = [ @$enders ]; - }; - }; - - - - -############################################################################### -# Group: Parsing Functions - - -# -# Function: ParseFile -# -# Parses the passed source file, sending comments acceptable for documentation to <NaturalDocs::Parser->OnComment()> -# and all other sections to <OnCode()>. -# -# Parameters: -# -# sourceFile - The <FileName> of the source file to parse. -# topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file. -# -# Returns: -# -# Since this class cannot automatically document the code or generate a scope record, it always returns ( undef, undef ). -# -sub ParseFile #(sourceFile, topicsList) - { - my ($self, $sourceFile, $topicsList) = @_; - - open(SOURCEFILEHANDLE, '<' . $sourceFile) - or die "Couldn't open input file " . $sourceFile . "\n"; - - my @commentLines; - my @codeLines; - my $lastCommentTopicCount = 0; - - if ($self->Name() eq 'Text File') - { - my $line = <SOURCEFILEHANDLE>; - - # On the very first line, remove a Unicode BOM if present. Information on it available at: - # http://www.unicode.org/faq/utf_bom.html#BOM - $line =~ s/^\xEF\xBB\xBF//; - - while ($line) - { - ::XChomp(\$line); - push @commentLines, $line; - $line = <SOURCEFILEHANDLE>; - }; - - NaturalDocs::Parser->OnComment(\@commentLines, 1); - } - - else - { - my $line = <SOURCEFILEHANDLE>; - my $lineNumber = 1; - - # On the very first line, remove a Unicode BOM if present. Information on it available at: - # http://www.unicode.org/faq/utf_bom.html#BOM - $line =~ s/^\xEF\xBB\xBF//; - - while (defined $line) - { - ::XChomp(\$line); - my $originalLine = $line; - - - # Retrieve single line comments. This leaves $line at the next line. - - if ($self->StripOpeningSymbols(\$line, $self->LineCommentSymbols())) - { - do - { - push @commentLines, $line; - $line = <SOURCEFILEHANDLE>; - - if (!defined $line) - { goto EndDo; }; - - ::XChomp(\$line); - } - while ($self->StripOpeningSymbols(\$line, $self->LineCommentSymbols())); - - EndDo: # I hate Perl sometimes. - } - - - # Retrieve multiline comments. This leaves $line at the next line. - - elsif (my $closingSymbol = $self->StripOpeningBlockSymbols(\$line, $self->BlockCommentSymbols())) - { - # Note that it is possible for a multiline comment to start correctly but not end so. We want those comments to stay in - # the code. For example, look at this prototype with this splint annotation: - # - # int get_array(integer_t id, - # /*@out@*/ array_t array); - # - # The annotation starts correctly but doesn't end so because it is followed by code on the same line. - - my $lineRemainder; - - for (;;) - { - $lineRemainder = $self->StripClosingSymbol(\$line, $closingSymbol); - - push @commentLines, $line; - - # If we found an end comment symbol... - if (defined $lineRemainder) - { last; }; - - $line = <SOURCEFILEHANDLE>; - - if (!defined $line) - { last; }; - - ::XChomp(\$line); - }; - - if ($lineRemainder !~ /^[ \t]*$/) - { - # If there was something past the closing symbol this wasn't an acceptable comment, so move the lines to code. - push @codeLines, @commentLines; - @commentLines = ( ); - }; - - $line = <SOURCEFILEHANDLE>; - } - - - # Otherwise just add it to the code. - - else - { - push @codeLines, $line; - $line = <SOURCEFILEHANDLE>; - }; - - - # If there were comments, send them to Parser->OnComment(). - - if (scalar @commentLines) - { - # First process any code lines before the comment. - if (scalar @codeLines) - { - $self->OnCode(\@codeLines, $lineNumber, $topicsList, $lastCommentTopicCount); - $lineNumber += scalar @codeLines; - @codeLines = ( ); - }; - - $lastCommentTopicCount = NaturalDocs::Parser->OnComment(\@commentLines, $lineNumber); - $lineNumber += scalar @commentLines; - @commentLines = ( ); - }; - - }; # while (defined $line) - - - # Clean up any remaining code. - if (scalar @codeLines) - { - $self->OnCode(\@codeLines, $lineNumber, $topicsList, $lastCommentTopicCount); - @codeLines = ( ); - }; - - }; - - close(SOURCEFILEHANDLE); - - return ( undef, undef ); - }; - - -# -# Function: OnCode -# -# Called whenever a section of code is encountered by the parser. Is used to find the prototype of the last topic created. -# -# Parameters: -# -# codeLines - The source code as an arrayref of lines. -# codeLineNumber - The line number of the first line of code. -# topicList - A reference to the list of <NaturalDocs::Parser::ParsedTopics> being built by the file. -# lastCommentTopicCount - The number of Natural Docs topics that were created by the last comment. -# -sub OnCode #(codeLines, codeLineNumber, topicList, lastCommentTopicCount) - { - my ($self, $codeLines, $codeLineNumber, $topicList, $lastCommentTopicCount) = @_; - - if ($lastCommentTopicCount && defined $self->PrototypeEndersFor($topicList->[-1]->Type())) - { - my $lineIndex = 0; - my $prototype; - - # Skip all blank lines before a prototype. - while ($lineIndex < scalar @$codeLines && $codeLines->[$lineIndex] =~ /^[ \t]*$/) - { $lineIndex++; }; - - my @tokens; - my $tokenIndex = 0; - - my @brackets; - my $enders = $self->PrototypeEndersFor($topicList->[-1]->Type()); - - # Add prototype lines until we reach the end of the prototype or the end of the code lines. - while ($lineIndex < scalar @$codeLines) - { - my $line = $self->RemoveLineExtender($codeLines->[$lineIndex] . "\n"); - - push @tokens, $line =~ /([^\(\)\[\]\{\}\<\>]+|.)/g; - - while ($tokenIndex < scalar @tokens) - { - # If we're not inside brackets, check for ender symbols. - if (!scalar @brackets) - { - my $startingIndex = 0; - my $testPrototype; - - for (;;) - { - my ($enderIndex, $ender) = ::FindFirstSymbol($tokens[$tokenIndex], $enders, $startingIndex); - - if ($enderIndex == -1) - { last; } - else - { - # We do this here so we don't duplicate prototype for every single token. Just the first time an ender symbol - # is found in one. - if (!defined $testPrototype) - { $testPrototype = $prototype; }; - - $testPrototype .= substr($tokens[$tokenIndex], $startingIndex, $enderIndex - $startingIndex); - - my $enderResult = $self->OnPrototypeEnd($topicList->[-1]->Type(), \$testPrototype, $ender); - - if ($enderResult == ENDER_IGNORE()) - { - $testPrototype .= $ender; - $startingIndex = $enderIndex + length($ender); - } - elsif ($enderResult == ENDER_REVERT_TO_ACCEPTED()) - { - return; - } - else # ENDER_ACCEPT || ENDER_ACCEPT_AND_CONTINUE - { - my $titleInPrototype = $topicList->[-1]->Title(); - - # Strip parenthesis so Function(2) and Function(int, int) will still match Function(anything). - $titleInPrototype =~ s/[\t ]*\([^\(]*$//; - - if (index($testPrototype, $titleInPrototype) != -1) - { - $topicList->[-1]->SetPrototype( $self->NormalizePrototype($testPrototype) ); - }; - - if ($enderResult == ENDER_ACCEPT()) - { return; } - else # ENDER_ACCEPT_AND_CONTINUE - { - $testPrototype .= $ender; - $startingIndex = $enderIndex + length($ender); - }; - }; - }; - }; - } - - # If we are inside brackets, check for closing symbols. - elsif ( ($tokens[$tokenIndex] eq ')' && $brackets[-1] eq '(') || - ($tokens[$tokenIndex] eq ']' && $brackets[-1] eq '[') || - ($tokens[$tokenIndex] eq '}' && $brackets[-1] eq '{') || - ($tokens[$tokenIndex] eq '>' && $brackets[-1] eq '<') ) - { - pop @brackets; - }; - - # Check for opening brackets. - if ($tokens[$tokenIndex] =~ /^[\(\[\{\<]$/) - { - push @brackets, $tokens[$tokenIndex]; - }; - - $prototype .= $tokens[$tokenIndex]; - $tokenIndex++; - }; - - $lineIndex++; - }; - - # If we got out of that while loop by running out of lines, there was no prototype. - }; - }; - - -use constant ENDER_ACCEPT => 1; -use constant ENDER_IGNORE => 2; -use constant ENDER_ACCEPT_AND_CONTINUE => 3; -use constant ENDER_REVERT_TO_ACCEPTED => 4; - -# -# Function: OnPrototypeEnd -# -# Called whenever the end of a prototype is found so that there's a chance for derived classes to mark false positives. -# -# Parameters: -# -# type - The <TopicType> of the prototype. -# prototypeRef - A reference to the prototype so far, minus the ender in dispute. -# ender - The ender symbol. -# -# Returns: -# -# ENDER_ACCEPT - The ender is accepted and the prototype is finished. -# ENDER_IGNORE - The ender is rejected and parsing should continue. Note that the prototype will be rejected as a whole -# if all enders are ignored before reaching the end of the code. -# ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is. However, the prototype might -# also continue on so continue parsing. If there is no accepted ender between here and -# the end of the code this version will be accepted instead. -# ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed. Use the last accepted -# version and end parsing. -# -sub OnPrototypeEnd #(type, prototypeRef, ender) - { - return ENDER_ACCEPT(); - }; - - -# -# Function: RemoveLineExtender -# -# If the passed line has a line extender, returns it without the extender or the line break that follows. If it doesn't, or there are -# no line extenders defined, returns the passed line unchanged. -# -sub RemoveLineExtender #(line) - { - my ($self, $line) = @_; - - if (defined $self->LineExtender()) - { - my $lineExtenderIndex = index($line, $self->LineExtender()); - - if ($lineExtenderIndex != -1 && - substr($line, $lineExtenderIndex + length($self->LineExtender())) =~ /^[ \t]*\n$/) - { - $line = substr($line, 0, $lineExtenderIndex) . ' '; - }; - }; - - return $line; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Languages/Tcl.pm b/docs/doctool/Modules/NaturalDocs/Languages/Tcl.pm deleted file mode 100644 index 846937bd..00000000 --- a/docs/doctool/Modules/NaturalDocs/Languages/Tcl.pm +++ /dev/null @@ -1,219 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Languages::Tcl -# -############################################################################### -# -# A subclass to handle the language variations of Tcl. -# -############################################################################### - -# 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::Languages::Tcl; - -use base 'NaturalDocs::Languages::Simple'; - - -# -# bool: pastFirstBrace -# -# Whether we've past the first brace in a function prototype or not. -# -my $pastFirstBrace; - - -# -# Function: OnCode -# -# This is just overridden to reset <pastFirstBrace>. -# -sub OnCode #(...) - { - my ($self, @params) = @_; - - $pastFirstBrace = 0; - - return $self->SUPER::OnCode(@params); - }; - - -# -# Function: OnPrototypeEnd -# -# Tcl's function syntax is shown below. -# -# > proc [name] { [params] } { [code] } -# -# The opening brace is one of the prototype enders. We need to allow the first opening brace because it contains the -# parameters. -# -# Also, the parameters may have braces within them. I've seen one that used { seconds 20 } as a parameter. -# -# Parameters: -# -# type - The <TopicType> of the prototype. -# prototypeRef - A reference to the prototype so far, minus the ender in dispute. -# ender - The ender symbol. -# -# Returns: -# -# ENDER_ACCEPT - The ender is accepted and the prototype is finished. -# ENDER_IGNORE - The ender is rejected and parsing should continue. Note that the prototype will be rejected as a whole -# if all enders are ignored before reaching the end of the code. -# ENDER_ACCEPT_AND_CONTINUE - The ender is accepted so the prototype may stand as is. However, the prototype might -# also continue on so continue parsing. If there is no accepted ender between here and -# the end of the code this version will be accepted instead. -# ENDER_REVERT_TO_ACCEPTED - The expedition from ENDER_ACCEPT_AND_CONTINUE failed. Use the last accepted -# version and end parsing. -# -sub OnPrototypeEnd #(type, prototypeRef, ender) - { - my ($self, $type, $prototypeRef, $ender) = @_; - - if ($type eq ::TOPIC_FUNCTION() && $ender eq '{' && !$pastFirstBrace) - { - $pastFirstBrace = 1; - return ::ENDER_IGNORE(); - } - else - { return ::ENDER_ACCEPT(); }; - }; - - -# -# Function: ParsePrototype -# -# Parses the prototype and returns it as a <NaturalDocs::Languages::Prototype> object. -# -# Parameters: -# -# type - The <TopicType>. -# prototype - The text prototype. -# -# Returns: -# -# A <NaturalDocs::Languages::Prototype> object. -# -sub ParsePrototype #(type, prototype) - { - my ($self, $type, $prototype) = @_; - - if ($type ne ::TOPIC_FUNCTION()) - { - my $object = NaturalDocs::Languages::Prototype->New($prototype); - return $object; - }; - - - # Parse the parameters out of the prototype. - - my @tokens = $prototype =~ /([^\{\}\ ]+|.)/g; - - my $parameter; - my @parameterLines; - - my $braceLevel = 0; - - my ($beforeParameters, $afterParameters, $finishedParameters); - - foreach my $token (@tokens) - { - if ($finishedParameters) - { $afterParameters .= $token; } - - elsif ($token eq '{') - { - if ($braceLevel == 0) - { $beforeParameters .= $token; } - - else # braceLevel > 0 - { $parameter .= $token; }; - - $braceLevel++; - } - - elsif ($token eq '}') - { - if ($braceLevel == 1) - { - if ($parameter && $parameter ne ' ') - { push @parameterLines, $parameter; }; - - $finishedParameters = 1; - $afterParameters .= $token; - - $braceLevel--; - } - elsif ($braceLevel > 1) - { - $parameter .= $token; - $braceLevel--; - }; - } - - elsif ($token eq ' ') - { - if ($braceLevel == 1) - { - if ($parameter) - { push @parameterLines, $parameter; }; - - $parameter = undef; - } - elsif ($braceLevel > 1) - { - $parameter .= $token; - } - else - { - $beforeParameters .= $token; - }; - } - - else - { - if ($braceLevel > 0) - { $parameter .= $token; } - else - { $beforeParameters .= $token; }; - }; - }; - - foreach my $part (\$beforeParameters, \$afterParameters) - { - $$part =~ s/^ //; - $$part =~ s/ $//; - }; - - my $prototypeObject = NaturalDocs::Languages::Prototype->New($beforeParameters, $afterParameters); - - - # Parse the actual parameters. - - foreach my $parameterLine (@parameterLines) - { - $prototypeObject->AddParameter( $self->ParseParameterLine($parameterLine) ); - }; - - return $prototypeObject; - }; - - -# -# Function: ParseParameterLine -# -# Parses a prototype parameter line and returns it as a <NaturalDocs::Languages::Prototype::Parameter> object. -# -sub ParseParameterLine #(line) - { - my ($self, $line) = @_; - return NaturalDocs::Languages::Prototype::Parameter->New(undef, undef, $line, undef, undef, undef); - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Menu.pm b/docs/doctool/Modules/NaturalDocs/Menu.pm deleted file mode 100644 index e2cb709c..00000000 --- a/docs/doctool/Modules/NaturalDocs/Menu.pm +++ /dev/null @@ -1,3168 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Menu -# -############################################################################### -# -# A package handling the menu's contents and state. -# -# Usage and Dependencies: -# -# - The <Event Handlers> can be called by <NaturalDocs::Project> immediately. -# -# - Prior to initialization, <NaturalDocs::Project> must be initialized, and all files that have been changed must be run -# through <NaturalDocs::Parser->ParseForInformation()>. -# -# - To initialize, call <LoadAndUpdate()>. Afterwards, all other functions are available. Also, <LoadAndUpdate()> will -# call <NaturalDocs::Settings->GenerateDirectoryNames()>. -# -# - To save the changes back to disk, call <Save()>. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - -use Tie::RefHash; - -use NaturalDocs::Menu::Entry; - -use strict; -use integer; - -package NaturalDocs::Menu; - - -# -# Constants: Constants -# -# MAXFILESINGROUP - The maximum number of file entries that can be present in a group before it becomes a candidate for -# sub-grouping. -# MINFILESINNEWGROUP - The minimum number of file entries that must be present in a group before it will be automatically -# created. This is *not* the number of files that must be in a group before it's deleted. -# -use constant MAXFILESINGROUP => 6; -use constant MINFILESINNEWGROUP => 3; - - -############################################################################### -# Group: Variables - - -# -# bool: hasChanged -# -# Whether the menu changed or not, regardless of why. -# -my $hasChanged; - - -# -# Object: menu -# -# The parsed menu file. Is stored as a <MENU_GROUP> <NaturalDocs::Menu::Entry> object, with the top-level entries being -# stored as the group's content. This is done because it makes a number of functions simpler to implement, plus it allows group -# flags to be set on the top-level. However, it is exposed externally via <Content()> as an arrayref. -# -# This structure will only contain objects for <MENU_FILE>, <MENU_GROUP>, <MENU_TEXT>, <MENU_LINK>, and -# <MENU_INDEX> entries. Other types, such as <MENU_TITLE>, are stored in variables such as <title>. -# -my $menu; - -# -# hash: defaultTitlesChanged -# -# An existence hash of default titles that have changed, since <OnDefaultTitleChange()> will be called before -# <LoadAndUpdate()>. Collects them to be applied later. The keys are the <FileNames>. -# -my %defaultTitlesChanged; - -# -# String: title -# -# The title of the menu. -# -my $title; - -# -# String: subTitle -# -# The sub-title of the menu. -# -my $subTitle; - -# -# String: footer -# -# The footer for the documentation. -# -my $footer; - -# -# hash: indexes -# -# An existence hash of all the defined index <TopicTypes> appearing in the menu. -# -my %indexes; - -# -# hash: previousIndexes -# -# An existence hash of all the index <TopicTypes> that appeared in the menu last time. -# -my %previousIndexes; - -# -# hash: bannedIndexes -# -# An existence hash of all the index <TopicTypes> that the user has manually deleted, and thus should not be added back to -# the menu automatically. -# -my %bannedIndexes; - - -############################################################################### -# Group: Files - -# -# File: Menu.txt -# -# The file used to generate the menu. -# -# Format: -# -# The file is plain text. Blank lines can appear anywhere and are ignored. Tags and their content must be completely -# contained on one line with the exception of Group's braces. -# -# > # [comment] -# -# The file supports single-line comments via #. They can appear alone on a line or after content. -# -# > Format: [version] -# > Title: [title] -# > SubTitle: [subtitle] -# > Footer: [footer] -# -# The file format version, menu title, subtitle, and footer are specified as above. Each can only be specified once, with -# subsequent ones being ignored. Subtitle is ignored if Title is not present. Format must be the first entry in the file. If it's -# not present, it's assumed the menu is from version 0.95 or earlier, since it was added with 1.0. -# -# > File: [title] ([file name]) -# > File: [title] (auto-title, [file name]) -# > File: [title] (no auto-title, [file name]) -# -# Files are specified as above. If there is only one input directory, file names are relative. Otherwise they are absolute. -# If "no auto-title" is specified, the title on the line is used. If not, the title is ignored and the -# default file title is used instead. Auto-title defaults to on, so specifying "auto-title" is for compatibility only. -# -# > Group: [title] -# > Group: [title] { ... } -# -# Groups are specified as above. If no braces are specified, the group's content is everything that follows until the end of the -# file, the next group (braced or unbraced), or the closing brace of a parent group. Group braces are the only things in this -# file that can span multiple lines. -# -# There is no limitations on where the braces can appear. The opening brace can appear after the group tag, on its own line, -# or preceding another tag on a line. Similarly, the closing brace can appear after another tag or on its own line. Being -# bitchy here would just get in the way of quick and dirty editing; the package will clean it up automatically when it writes it -# back to disk. -# -# > Text: [text] -# -# Arbitrary text is specified as above. As with other tags, everything must be contained on the same line. -# -# > Link: [URL] -# > Link: [title] ([URL]) -# -# External links can be specified as above. If the titled form is not used, the URL is used as the title. -# -# > Index: [name] -# > [topic type name] Index: [name] -# -# Indexes are specified as above. The topic type names can be either singular or plural. General is assumed if not specified. -# -# > Don't Index: [topic type name] -# > Don't Index: [topic type name], [topic type name], ... -# -# The option above prevents indexes that exist but are not on the menu from being automatically added. -# -# > Data: [number]([obscured data]) -# -# Used to store non-user editable data. -# -# > Data: 1([obscured: [directory name]///[input directory]]) -# -# When there is more than one directory, these lines store the input directories used in the last run and their names. This -# allows menu files to be shared across machines since the names will be consistent and the directories can be used to convert -# filenames to the local machine's paths. We don't want this user-editable because they may think changing it changes the -# input directories, when it doesn't. Also, changing it without changing all the paths screws up resolving. -# -# > Data: 2([obscured: [directory name]) -# -# When there is only one directory and its name is not "default", this stores the name. -# -# -# Revisions: -# -# 1.3: -# -# - File names are now relative again if there is only one input directory. -# - Data: 2(...) added. -# - Can't use synonyms like "copyright" for "footer" or "sub-title" for "subtitle". -# - "Don't Index" line now requires commas to separate them, whereas it tolerated just spaces before. -# -# 1.16: -# -# - File names are now absolute instead of relative. Prior to 1.16 only one input directory was allowed, so they could be -# relative. -# - Data keywords introduced to store input directories and their names. -# -# 1.14: -# -# - Renamed this file from NaturalDocs_Menu.txt to Menu.txt. -# -# 1.1: -# -# - Added the "don't index" line. -# -# This is also the point where indexes were automatically added and removed, so all index entries from prior revisions -# were manually added and are not guaranteed to contain anything. -# -# 1.0: -# -# - Added the format line. -# - Added the "no auto-title" attribute. -# - Changed the file entry default to auto-title. -# -# This is also the point where auto-organization and better auto-titles were introduced. All groups in prior revisions were -# manually added, with the exception of a top-level Other group where new files were automatically added if there were -# groups defined. -# -# Break in support: -# -# Releases prior to 1.0 are no longer supported. Why? -# -# - They don't have a Format: line, which is required by <NaturalDocs::ConfigFile>, although I could work around this -# if I needed to. -# - No significant number of downloads for pre-1.0 releases. -# - Code simplification. I don't have to bridge the conversion from manual-only menu organization to automatic. -# -# 0.9: -# -# - Added index entries. -# - -# -# File: PreviousMenuState.nd -# -# The file used to store the previous state of the menu so as to detect changes. -# -# -# Format: -# -# > [BINARY_FORMAT] -# > [VersionInt: app version] -# -# First is the standard <BINARY_FORMAT> <VersionInt> header. -# -# > [UInt8: 0 (end group)] -# > [UInt8: MENU_FILE] [UInt8: noAutoTitle] [AString16: title] [AString16: target] -# > [UInt8: MENU_GROUP] [AString16: title] -# > [UInt8: MENU_INDEX] [AString16: title] [AString16: topic type] -# > [UInt8: MENU_LINK] [AString16: title] [AString16: url] -# > [UInt8: MENU_TEXT] [AString16: text] -# -# The first UInt8 of each following line is either zero or one of the <Menu Entry Types>. What follows is contextual. -# -# There are no entries for title, subtitle, or footer. Only the entries present in <menu>. -# -# See Also: -# -# <File Format Conventions> -# -# Dependencies: -# -# - Because the type is represented by a UInt8, the <Menu Entry Types> must all be <= 255. -# -# Revisions: -# -# 1.3: -# -# - The topic type following the <MENU_INDEX> entries were changed from UInt8s to AString16s, since <TopicTypes> -# were switched from integer constants to strings. You can still convert the old to the new via -# <NaturalDocs::Topics->TypeFromLegacy()>. -# -# 1.16: -# -# - The file targets are now absolute. Prior to 1.16, they were relative to the input directory since only one was allowed. -# -# 1.14: -# -# - The file was renamed from NaturalDocs.m to PreviousMenuState.nd and moved into the Data subdirectory. -# -# 1.0: -# -# - The file's format was completely redone. Prior to 1.0, the file was a text file consisting of the app version and a line -# which was a tab-separated list of the indexes present in the menu. * meant the general index. -# -# Break in support: -# -# Pre-1.0 files are no longer supported. There was no significant number of downloads for pre-1.0 releases, and this -# eliminates a separate code path for them. -# -# 0.95: -# -# - Change the file version to match the app version. Prior to 0.95, the version line was 1. Test for "1" instead of "1.0" to -# distinguish. -# -# 0.9: -# -# - The file was added to the project. Prior to 0.9, it didn't exist. -# - - -############################################################################### -# Group: File Functions - -# -# Function: LoadAndUpdate -# -# Loads the menu file from disk and updates it. Will add, remove, rearrange, and remove auto-titling from entries as -# necessary. Will also call <NaturalDocs::Settings->GenerateDirectoryNames()>. -# -sub LoadAndUpdate - { - my ($self) = @_; - - my ($inputDirectoryNames, $relativeFiles, $onlyDirectoryName) = $self->LoadMenuFile(); - - my $errorCount = NaturalDocs::ConfigFile->ErrorCount(); - if ($errorCount) - { - NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile(); - NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors') - . ' in ' . NaturalDocs::Project->MenuFile()); - }; - - if ($relativeFiles) - { - my $inputDirectory = $self->ResolveRelativeInputDirectories($onlyDirectoryName); - - if ($onlyDirectoryName) - { $inputDirectoryNames = { $inputDirectory => $onlyDirectoryName }; }; - } - else - { $self->ResolveInputDirectories($inputDirectoryNames); }; - - NaturalDocs::Settings->GenerateDirectoryNames($inputDirectoryNames); - - my $filesInMenu = $self->FilesInMenu(); - - my ($previousMenu, $previousIndexes, $previousFiles) = $self->LoadPreviousMenuStateFile(); - - if (defined $previousIndexes) - { %previousIndexes = %$previousIndexes; }; - - if (defined $previousFiles) - { $self->LockUserTitleChanges($previousFiles); }; - - # Don't need these anymore. We keep this level of detail because it may be used more in the future. - $previousMenu = undef; - $previousFiles = undef; - $previousIndexes = undef; - - # We flag title changes instead of actually performing them at this point for two reasons. First, contents of groups are still - # subject to change, which would affect the generated titles. Second, we haven't detected the sort order yet. Changing titles - # could make groups appear unalphabetized when they were beforehand. - - my $updateAllTitles; - - # If the menu file changed, we can't be sure which groups changed and which didn't without a comparison, which really isn't - # worth the trouble. So we regenerate all the titles instead. - if (NaturalDocs::Project->MenuFileStatus() == ::FILE_CHANGED()) - { $updateAllTitles = 1; } - else - { $self->FlagAutoTitleChanges(); }; - - # We add new files before deleting old files so their presence still affects the grouping. If we deleted old files first, it could - # throw off where to place the new ones. - - $self->AutoPlaceNewFiles($filesInMenu); - - my $numberRemoved = $self->RemoveDeadFiles(); - - $self->CheckForTrashedMenu(scalar keys %$filesInMenu, $numberRemoved); - - # Don't ban indexes if they deleted Menu.txt. They may have not deleted PreviousMenuState.nd and we don't want everything - # to be banned because of it. - if (NaturalDocs::Project->MenuFileStatus() != ::FILE_DOESNTEXIST()) - { $self->BanAndUnbanIndexes(); }; - - # Index groups need to be detected before adding new ones. - - $self->DetectIndexGroups(); - - $self->AddAndRemoveIndexes(); - - # We wait until after new files are placed to remove dead groups because a new file may save a group. - - $self->RemoveDeadGroups(); - - $self->CreateDirectorySubGroups(); - - # We detect the sort before regenerating the titles so it doesn't get thrown off by changes. However, we do it after deleting - # dead entries and moving things into subgroups because their removal may bump it into a stronger sort category (i.e. - # SORTFILESANDGROUPS instead of just SORTFILES.) New additions don't factor into the sort. - - $self->DetectOrder($updateAllTitles); - - $self->GenerateAutoFileTitles($updateAllTitles); - - $self->ResortGroups($updateAllTitles); - - - # Don't need this anymore. - %defaultTitlesChanged = ( ); - }; - - -# -# Function: Save -# -# Writes the changes to the menu files. -# -sub Save - { - my ($self) = @_; - - if ($hasChanged) - { - $self->SaveMenuFile(); - $self->SavePreviousMenuStateFile(); - }; - }; - - -############################################################################### -# Group: Information Functions - -# -# Function: HasChanged -# -# Returns whether the menu has changed or not. -# -sub HasChanged - { return $hasChanged; }; - -# -# Function: Content -# -# Returns the parsed menu as an arrayref of <NaturalDocs::Menu::Entry> objects. Do not change the arrayref. -# -# The arrayref will only contain <MENU_FILE>, <MENU_GROUP>, <MENU_INDEX>, <MENU_TEXT>, and <MENU_LINK> -# entries. Entries such as <MENU_TITLE> are parsed out and are only accessible via functions such as <Title()>. -# -sub Content - { return $menu->GroupContent(); }; - -# -# Function: Title -# -# Returns the title of the menu, or undef if none. -# -sub Title - { return $title; }; - -# -# Function: SubTitle -# -# Returns the sub-title of the menu, or undef if none. -# -sub SubTitle - { return $subTitle; }; - -# -# Function: Footer -# -# Returns the footer of the documentation, or undef if none. -# -sub Footer - { return $footer; }; - -# -# Function: Indexes -# -# Returns an existence hashref of all the index <TopicTypes> appearing in the menu. Do not change the arrayref. -# -sub Indexes - { return \%indexes; }; - -# -# Function: PreviousIndexes -# -# Returns an existence hashref of all the index <TopicTypes> that previously appeared in the menu. Do not change the -# arrayref. -# -sub PreviousIndexes - { return \%previousIndexes; }; - - -# -# Function: FilesInMenu -# -# Returns a hashref of all the files present in the menu. The keys are the <FileNames>, and the values are references to their -# <NaturalDocs::Menu::Entry> objects. -# -sub FilesInMenu - { - my ($self) = @_; - - my @groupStack = ( $menu ); - my $filesInMenu = { }; - - while (scalar @groupStack) - { - my $currentGroup = pop @groupStack; - my $currentGroupContent = $currentGroup->GroupContent(); - - foreach my $entry (@$currentGroupContent) - { - if ($entry->Type() == ::MENU_GROUP()) - { push @groupStack, $entry; } - elsif ($entry->Type() == ::MENU_FILE()) - { $filesInMenu->{ $entry->Target() } = $entry; }; - }; - }; - - return $filesInMenu; - }; - - - -############################################################################### -# Group: Event Handlers -# -# These functions are called by <NaturalDocs::Project> only. You don't need to worry about calling them. For example, when -# changing the default menu title of a file, you only need to call <NaturalDocs::Project->SetDefaultMenuTitle()>. That function -# will handle calling <OnDefaultTitleChange()>. - - -# -# Function: OnDefaultTitleChange -# -# Called by <NaturalDocs::Project> if the default menu title of a source file has changed. -# -# Parameters: -# -# file - The source <FileName> that had its default menu title changed. -# -sub OnDefaultTitleChange #(file) - { - my ($self, $file) = @_; - - # Collect them for later. We'll deal with them in LoadAndUpdate(). - - $defaultTitlesChanged{$file} = 1; - }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: LoadMenuFile -# -# Loads and parses the menu file <Menu.txt>. This will fill <menu>, <title>, <subTitle>, <footer>, <indexes>, and -# <bannedIndexes>. If there are any errors in the file, they will be recorded with <NaturalDocs::ConfigFile->AddError()>. -# -# Returns: -# -# The array ( inputDirectories, relativeFiles, onlyDirectoryName ) or an empty array if the file doesn't exist. -# -# inputDirectories - A hashref of all the input directories and their names stored in the menu file. The keys are the -# directories and the values are their names. Undef if none. -# relativeFiles - Whether the menu uses relative file names. -# onlyDirectoryName - The name of the input directory if there is only one. -# -sub LoadMenuFile - { - my ($self) = @_; - - my $inputDirectories = { }; - my $relativeFiles; - my $onlyDirectoryName; - - # A stack of Menu::Entry object references as we move through the groups. - my @groupStack; - - $menu = NaturalDocs::Menu::Entry->New(::MENU_GROUP(), undef, undef, undef); - my $currentGroup = $menu; - - # Whether we're currently in a braceless group, since we'd have to find the implied end rather than an explicit one. - my $inBracelessGroup; - - # Whether we're right after a group token, which is the only place there can be an opening brace. - my $afterGroupToken; - - my $version; - - if ($version = NaturalDocs::ConfigFile->Open(NaturalDocs::Project->MenuFile(), 1)) - { - # We don't check if the menu file is from a future version because we can't just throw it out and regenerate it like we can - # with other data files. So we just keep going regardless. Any syntactic differences will show up as errors. - - while (my ($keyword, $value, $comment) = NaturalDocs::ConfigFile->GetLine()) - { - # Check for an opening brace after a group token. This has to be separate from the rest of the code because the flag - # needs to be reset after every line. - if ($afterGroupToken) - { - $afterGroupToken = undef; - - if ($keyword eq '{') - { - $inBracelessGroup = undef; - next; - } - else - { $inBracelessGroup = 1; }; - }; - - - # Now on to the real code. - - if ($keyword eq 'file') - { - my $flags = 0; - - if ($value =~ /^([^\(\)]+?) ?\((.+)\)$/) - { - my ($title, $file) = ($1, $2); - - # Check for auto-title modifier. - if ($file =~ /^((?:no )?auto-title, ?)(.+)$/i) - { - my $modifier; - ($modifier, $file) = ($1, $2); - - if ($modifier =~ /^no/i) - { $flags |= ::MENU_FILE_NOAUTOTITLE(); }; - }; - - my $entry = NaturalDocs::Menu::Entry->New(::MENU_FILE(), $title, $file, $flags); - - $currentGroup->PushToGroup($entry); - } - else - { NaturalDocs::ConfigFile->AddError('File lines must be in the format "File: [title] ([location])"'); }; - } - - - elsif ($keyword eq 'group') - { - # End a braceless group, if we were in one. - if ($inBracelessGroup) - { - $currentGroup = pop @groupStack; - $inBracelessGroup = undef; - }; - - my $entry = NaturalDocs::Menu::Entry->New(::MENU_GROUP(), $value, undef, undef); - - $currentGroup->PushToGroup($entry); - - push @groupStack, $currentGroup; - $currentGroup = $entry; - - $afterGroupToken = 1; - } - - - elsif ($keyword eq '{') - { - NaturalDocs::ConfigFile->AddError('Opening braces are only allowed after Group tags.'); - } - - - elsif ($keyword eq '}') - { - # End a braceless group, if we were in one. - if ($inBracelessGroup) - { - $currentGroup = pop @groupStack; - $inBracelessGroup = undef; - }; - - # End a braced group too. - if (scalar @groupStack) - { $currentGroup = pop @groupStack; } - else - { NaturalDocs::ConfigFile->AddError('Unmatched closing brace.'); }; - } - - - elsif ($keyword eq 'title') - { - if (!defined $title) - { $title = $value; } - else - { NaturalDocs::ConfigFile->AddError('Title can only be defined once.'); }; - } - - - elsif ($keyword eq 'subtitle') - { - if (defined $title) - { - if (!defined $subTitle) - { $subTitle = $value; } - else - { NaturalDocs::ConfigFile->AddError('SubTitle can only be defined once.'); }; - } - else - { NaturalDocs::ConfigFile->AddError('Title must be defined before SubTitle.'); }; - } - - - elsif ($keyword eq 'footer') - { - if (!defined $footer) - { $footer = $value; } - else - { NaturalDocs::ConfigFile->AddError('Footer can only be defined once.'); }; - } - - - elsif ($keyword eq 'text') - { - $currentGroup->PushToGroup( NaturalDocs::Menu::Entry->New(::MENU_TEXT(), $value, undef, undef) ); - } - - - elsif ($keyword eq 'link') - { - my ($title, $url); - - if ($value =~ /^([^\(\)]+?) ?\(([^\)]+)\)$/) - { - ($title, $url) = ($1, $2); - } - elsif (defined $comment) - { - $value .= $comment; - - if ($value =~ /^([^\(\)]+?) ?\(([^\)]+)\) ?(?:#.*)?$/) - { - ($title, $url) = ($1, $2); - }; - }; - - if ($title) - { - $currentGroup->PushToGroup( NaturalDocs::Menu::Entry->New(::MENU_LINK(), $title, $url, undef) ); - } - else - { NaturalDocs::ConfigFile->AddError('Link lines must be in the format "Link: [title] ([url])"'); }; - } - - - elsif ($keyword eq 'data') - { - $value =~ /^(\d)\((.*)\)$/; - my ($number, $data) = ($1, $2); - - $data = NaturalDocs::ConfigFile->Unobscure($data); - - # The input directory naming convention changed with version 1.32, but NaturalDocs::Settings will handle that - # automatically. - - if ($number == 1) - { - my ($dirName, $inputDir) = split(/\/\/\//, $data, 2); - $inputDirectories->{$inputDir} = $dirName; - } - elsif ($number == 2) - { $onlyDirectoryName = $data; }; - # Ignore other numbers because it may be from a future format and we don't want to make the user delete it - # manually. - } - - elsif ($keyword eq "don't index") - { - my @indexes = split(/, ?/, $value); - - foreach my $index (@indexes) - { - my $indexType = NaturalDocs::Topics->TypeFromName($index); - - if (defined $indexType) - { $bannedIndexes{$indexType} = 1; }; - }; - } - - elsif ($keyword eq 'index') - { - my $entry = NaturalDocs::Menu::Entry->New(::MENU_INDEX(), $value, ::TOPIC_GENERAL(), undef); - $currentGroup->PushToGroup($entry); - - $indexes{::TOPIC_GENERAL()} = 1; - } - - elsif (substr($keyword, -6) eq ' index') - { - my $index = substr($keyword, 0, -6); - my ($indexType, $indexInfo) = NaturalDocs::Topics->NameInfo($index); - - if (defined $indexType) - { - if ($indexInfo->Index()) - { - $indexes{$indexType} = 1; - $currentGroup->PushToGroup( - NaturalDocs::Menu::Entry->New(::MENU_INDEX(), $value, $indexType, undef) ); - } - else - { - # If it's on the menu but isn't indexable, the topic setting may have changed out from under it. - $hasChanged = 1; - }; - } - else - { - NaturalDocs::ConfigFile->AddError($index . ' is not a valid index type.'); - }; - } - - else - { - NaturalDocs::ConfigFile->AddError(ucfirst($keyword) . ' is not a valid keyword.'); - }; - }; - - - # End a braceless group, if we were in one. - if ($inBracelessGroup) - { - $currentGroup = pop @groupStack; - $inBracelessGroup = undef; - }; - - # Close up all open groups. - my $openGroups = 0; - while (scalar @groupStack) - { - $currentGroup = pop @groupStack; - $openGroups++; - }; - - if ($openGroups == 1) - { NaturalDocs::ConfigFile->AddError('There is an unclosed group.'); } - elsif ($openGroups > 1) - { NaturalDocs::ConfigFile->AddError('There are ' . $openGroups . ' unclosed groups.'); }; - - - if (!scalar keys %$inputDirectories) - { - $inputDirectories = undef; - $relativeFiles = 1; - }; - - NaturalDocs::ConfigFile->Close(); - - return ($inputDirectories, $relativeFiles, $onlyDirectoryName); - } - - else - { return ( ); }; - }; - - -# -# Function: SaveMenuFile -# -# Saves the current menu to <Menu.txt>. -# -sub SaveMenuFile - { - my ($self) = @_; - - open(MENUFILEHANDLE, '>' . NaturalDocs::Project->MenuFile()) - or die "Couldn't save menu file " . NaturalDocs::Project->MenuFile() . "\n"; - - - print MENUFILEHANDLE - "Format: " . NaturalDocs::Settings->TextAppVersion() . "\n\n\n"; - - my $inputDirs = NaturalDocs::Settings->InputDirectories(); - - - if (defined $title) - { - print MENUFILEHANDLE 'Title: ' . $title . "\n"; - - if (defined $subTitle) - { - print MENUFILEHANDLE 'SubTitle: ' . $subTitle . "\n"; - } - else - { - print MENUFILEHANDLE - "\n" - . "# You can also add a sub-title to your menu like this:\n" - . "# SubTitle: [subtitle]\n"; - }; - } - else - { - print MENUFILEHANDLE - "# You can add a title and sub-title to your menu like this:\n" - . "# Title: [project name]\n" - . "# SubTitle: [subtitle]\n"; - }; - - print MENUFILEHANDLE "\n"; - - if (defined $footer) - { - print MENUFILEHANDLE 'Footer: ' . $footer . "\n"; - } - else - { - print MENUFILEHANDLE - "# You can add a footer to your documentation like this:\n" - . "# Footer: [text]\n" - . "# If you want to add a copyright notice, this would be the place to do it.\n"; - }; - - print MENUFILEHANDLE "\n"; - - if (scalar keys %bannedIndexes) - { - print MENUFILEHANDLE - - "# These are indexes you deleted, so Natural Docs will not add them again\n" - . "# unless you remove them from this line.\n" - . "\n" - . "Don't Index: "; - - my $first = 1; - - foreach my $index (keys %bannedIndexes) - { - if (!$first) - { print MENUFILEHANDLE ', '; } - else - { $first = undef; }; - - print MENUFILEHANDLE NaturalDocs::Topics->NameOfType($index, 1); - }; - - print MENUFILEHANDLE "\n\n"; - }; - - - # Remember to keep lines below eighty characters. - - print MENUFILEHANDLE - "\n" - . "# --------------------------------------------------------------------------\n" - . "# \n" - . "# Cut and paste the lines below to change the order in which your files\n" - . "# appear on the menu. Don't worry about adding or removing files, Natural\n" - . "# Docs will take care of that.\n" - . "# \n" - . "# You can further organize the menu by grouping the entries. Add a\n" - . "# \"Group: [name] {\" line to start a group, and add a \"}\" to end it.\n" - . "# \n" - . "# You can add text and web links to the menu by adding \"Text: [text]\" and\n" - . "# \"Link: [name] ([URL])\" lines, respectively.\n" - . "# \n" - . "# The formatting and comments are auto-generated, so don't worry about\n" - . "# neatness when editing the file. Natural Docs will clean it up the next\n" - . "# time it is run. When working with groups, just deal with the braces and\n" - . "# forget about the indentation and comments.\n" - . "# \n"; - - if (scalar @$inputDirs > 1) - { - print MENUFILEHANDLE - "# You can use this file on other computers even if they use different\n" - . "# directories. As long as the command line points to the same source files,\n" - . "# Natural Docs will be able to correct the locations automatically.\n" - . "# \n"; - }; - - print MENUFILEHANDLE - "# --------------------------------------------------------------------------\n" - - . "\n\n"; - - - $self->WriteMenuEntries($menu->GroupContent(), \*MENUFILEHANDLE, undef, (scalar @$inputDirs == 1)); - - - if (scalar @$inputDirs > 1) - { - print MENUFILEHANDLE - "\n\n##### Do not change or remove these lines. #####\n"; - - foreach my $inputDir (@$inputDirs) - { - print MENUFILEHANDLE - 'Data: 1(' . NaturalDocs::ConfigFile->Obscure( NaturalDocs::Settings->InputDirectoryNameOf($inputDir) - . '///' . $inputDir ) . ")\n"; - }; - } - elsif (lc(NaturalDocs::Settings->InputDirectoryNameOf($inputDirs->[0])) != 1) - { - print MENUFILEHANDLE - "\n\n##### Do not change or remove this line. #####\n" - . 'Data: 2(' . NaturalDocs::ConfigFile->Obscure( NaturalDocs::Settings->InputDirectoryNameOf($inputDirs->[0]) ) . ")\n"; - } - - close(MENUFILEHANDLE); - }; - - -# -# Function: WriteMenuEntries -# -# A recursive function to write the contents of an arrayref of <NaturalDocs::Menu::Entry> objects to disk. -# -# Parameters: -# -# entries - The arrayref of menu entries to write. -# fileHandle - The handle to the output file. -# indentChars - The indentation _characters_ to add before each line. It is not the number of characters, it is the characters -# themselves. Use undef for none. -# relativeFiles - Whether to use relative file names. -# -sub WriteMenuEntries #(entries, fileHandle, indentChars, relativeFiles) - { - my ($self, $entries, $fileHandle, $indentChars, $relativeFiles) = @_; - my $lastEntryType; - - foreach my $entry (@$entries) - { - if ($entry->Type() == ::MENU_FILE()) - { - my $fileName; - - if ($relativeFiles) - { $fileName = (NaturalDocs::Settings->SplitFromInputDirectory($entry->Target()))[1]; } - else - { $fileName = $entry->Target(); }; - - print $fileHandle $indentChars . 'File: ' . $entry->Title() - . ' (' . ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE() ? 'no auto-title, ' : '') . $fileName . ")\n"; - } - elsif ($entry->Type() == ::MENU_GROUP()) - { - if (defined $lastEntryType && $lastEntryType != ::MENU_GROUP()) - { print $fileHandle "\n"; }; - - print $fileHandle $indentChars . 'Group: ' . $entry->Title() . " {\n\n"; - $self->WriteMenuEntries($entry->GroupContent(), $fileHandle, ' ' . $indentChars, $relativeFiles); - print $fileHandle ' ' . $indentChars . '} # Group: ' . $entry->Title() . "\n\n"; - } - elsif ($entry->Type() == ::MENU_TEXT()) - { - print $fileHandle $indentChars . 'Text: ' . $entry->Title() . "\n"; - } - elsif ($entry->Type() == ::MENU_LINK()) - { - print $fileHandle $indentChars . 'Link: ' . $entry->Title() . ' (' . $entry->Target() . ')' . "\n"; - } - elsif ($entry->Type() == ::MENU_INDEX()) - { - my $type; - if ($entry->Target() ne ::TOPIC_GENERAL()) - { - $type = NaturalDocs::Topics->NameOfType($entry->Target()) . ' '; - }; - - print $fileHandle $indentChars . $type . 'Index: ' . $entry->Title() . "\n"; - }; - - $lastEntryType = $entry->Type(); - }; - }; - - -# -# Function: LoadPreviousMenuStateFile -# -# Loads and parses the previous menu state file. -# -# Returns: -# -# The array ( previousMenu, previousIndexes, previousFiles ) or an empty array if there was a problem with the file. -# -# previousMenu - A <MENU_GROUP> <NaturalDocs::Menu::Entry> object, similar to <menu>, which contains the entire -# previous menu. -# previousIndexes - An existence hashref of the index <TopicTypes> present in the previous menu. -# previousFiles - A hashref of the files present in the previous menu. The keys are the <FileNames>, and the entries are -# references to its object in previousMenu. -# -sub LoadPreviousMenuStateFile - { - my ($self) = @_; - - my $fileIsOkay; - my $version; - my $previousStateFileName = NaturalDocs::Project->PreviousMenuStateFile(); - - if (open(PREVIOUSSTATEFILEHANDLE, '<' . $previousStateFileName)) - { - # See if it's binary. - binmode(PREVIOUSSTATEFILEHANDLE); - - my $firstChar; - read(PREVIOUSSTATEFILEHANDLE, $firstChar, 1); - - if ($firstChar == ::BINARY_FORMAT()) - { - $version = NaturalDocs::Version->FromBinaryFile(\*PREVIOUSSTATEFILEHANDLE); - - # Only the topic type format has changed since switching to binary, and we support both methods. - - if ($version <= NaturalDocs::Settings->AppVersion()) - { $fileIsOkay = 1; } - else - { close(PREVIOUSSTATEFILEHANDLE); }; - } - - else # it's not in binary - { close(PREVIOUSSTATEFILEHANDLE); }; - }; - - if ($fileIsOkay) - { - if (NaturalDocs::Project->MenuFileStatus() == ::FILE_CHANGED()) - { $hasChanged = 1; }; - - - my $menu = NaturalDocs::Menu::Entry->New(::MENU_GROUP(), undef, undef, undef); - my $indexes = { }; - my $files = { }; - - my @groupStack; - my $currentGroup = $menu; - my $raw; - - # [UInt8: type or 0 for end group] - - while (read(PREVIOUSSTATEFILEHANDLE, $raw, 1)) - { - my ($type, $flags, $title, $titleLength, $target, $targetLength); - $type = unpack('C', $raw); - - if ($type == 0) - { $currentGroup = pop @groupStack; } - - elsif ($type == ::MENU_FILE()) - { - # [UInt8: noAutoTitle] [AString16: title] [AString16: target] - - read(PREVIOUSSTATEFILEHANDLE, $raw, 3); - (my $noAutoTitle, $titleLength) = unpack('Cn', $raw); - - if ($noAutoTitle) - { $flags = ::MENU_FILE_NOAUTOTITLE(); }; - - read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength); - read(PREVIOUSSTATEFILEHANDLE, $raw, 2); - - $targetLength = unpack('n', $raw); - - read(PREVIOUSSTATEFILEHANDLE, $target, $targetLength); - } - - elsif ($type == ::MENU_GROUP()) - { - # [AString16: title] - - read(PREVIOUSSTATEFILEHANDLE, $raw, 2); - $titleLength = unpack('n', $raw); - - read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength); - } - - elsif ($type == ::MENU_INDEX()) - { - # [AString16: title] - - read(PREVIOUSSTATEFILEHANDLE, $raw, 2); - $titleLength = unpack('n', $raw); - - read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength); - - if ($version >= NaturalDocs::Version->FromString('1.3')) - { - # [AString16: topic type] - read(PREVIOUSSTATEFILEHANDLE, $raw, 2); - $targetLength = unpack('n', $raw); - - read(PREVIOUSSTATEFILEHANDLE, $target, $targetLength); - } - else - { - # [UInt8: topic type (0 for general)] - read(PREVIOUSSTATEFILEHANDLE, $raw, 1); - $target = unpack('C', $raw); - - $target = NaturalDocs::Topics->TypeFromLegacy($target); - }; - } - - elsif ($type == ::MENU_LINK()) - { - # [AString16: title] [AString16: url] - - read(PREVIOUSSTATEFILEHANDLE, $raw, 2); - $titleLength = unpack('n', $raw); - - read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength); - read(PREVIOUSSTATEFILEHANDLE, $raw, 2); - $targetLength = unpack('n', $raw); - - read(PREVIOUSSTATEFILEHANDLE, $target, $targetLength); - } - - elsif ($type == ::MENU_TEXT()) - { - # [AString16: text] - - read(PREVIOUSSTATEFILEHANDLE, $raw, 2); - $titleLength = unpack('n', $raw); - - read(PREVIOUSSTATEFILEHANDLE, $title, $titleLength); - }; - - - # The topic type of the index may have been removed. - - if ( !($type == ::MENU_INDEX() && !NaturalDocs::Topics->IsValidType($target)) ) - { - my $entry = NaturalDocs::Menu::Entry->New($type, $title, $target, ($flags || 0)); - $currentGroup->PushToGroup($entry); - - if ($type == ::MENU_FILE()) - { - $files->{$target} = $entry; - } - elsif ($type == ::MENU_GROUP()) - { - push @groupStack, $currentGroup; - $currentGroup = $entry; - } - elsif ($type == ::MENU_INDEX()) - { - $indexes->{$target} = 1; - }; - }; - - }; - - close(PREVIOUSSTATEFILEHANDLE); - - return ($menu, $indexes, $files); - } - else - { - $hasChanged = 1; - return ( ); - }; - }; - - -# -# Function: SavePreviousMenuStateFile -# -# Saves changes to <PreviousMenuState.nd>. -# -sub SavePreviousMenuStateFile - { - my ($self) = @_; - - open (PREVIOUSSTATEFILEHANDLE, '>' . NaturalDocs::Project->PreviousMenuStateFile()) - or die "Couldn't save " . NaturalDocs::Project->PreviousMenuStateFile() . ".\n"; - - binmode(PREVIOUSSTATEFILEHANDLE); - - print PREVIOUSSTATEFILEHANDLE '' . ::BINARY_FORMAT(); - - NaturalDocs::Version->ToBinaryFile(\*PREVIOUSSTATEFILEHANDLE, NaturalDocs::Settings->AppVersion()); - - $self->WritePreviousMenuStateEntries($menu->GroupContent(), \*PREVIOUSSTATEFILEHANDLE); - - close(PREVIOUSSTATEFILEHANDLE); - }; - - -# -# Function: WritePreviousMenuStateEntries -# -# A recursive function to write the contents of an arrayref of <NaturalDocs::Menu::Entry> objects to disk. -# -# Parameters: -# -# entries - The arrayref of menu entries to write. -# fileHandle - The handle to the output file. -# -sub WritePreviousMenuStateEntries #(entries, fileHandle) - { - my ($self, $entries, $fileHandle) = @_; - - foreach my $entry (@$entries) - { - if ($entry->Type() == ::MENU_FILE()) - { - # We need to do length manually instead of using n/A in the template because it's not supported in earlier versions - # of Perl. - - # [UInt8: MENU_FILE] [UInt8: noAutoTitle] [AString16: title] [AString16: target] - print $fileHandle pack('CCnA*nA*', ::MENU_FILE(), ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE() ? 1 : 0), - length($entry->Title()), $entry->Title(), - length($entry->Target()), $entry->Target()); - } - - elsif ($entry->Type() == ::MENU_GROUP()) - { - # [UInt8: MENU_GROUP] [AString16: title] - print $fileHandle pack('CnA*', ::MENU_GROUP(), length($entry->Title()), $entry->Title()); - $self->WritePreviousMenuStateEntries($entry->GroupContent(), $fileHandle); - print $fileHandle pack('C', 0); - } - - elsif ($entry->Type() == ::MENU_INDEX()) - { - # [UInt8: MENU_INDEX] [AString16: title] [AString16: topic type] - print $fileHandle pack('CnA*nA*', ::MENU_INDEX(), length($entry->Title()), $entry->Title(), - length($entry->Target()), $entry->Target()); - } - - elsif ($entry->Type() == ::MENU_LINK()) - { - # [UInt8: MENU_LINK] [AString16: title] [AString16: url] - print $fileHandle pack('CnA*nA*', ::MENU_LINK(), length($entry->Title()), $entry->Title(), - length($entry->Target()), $entry->Target()); - } - - elsif ($entry->Type() == ::MENU_TEXT()) - { - # [UInt8: MENU_TEXT] [AString16: hext] - print $fileHandle pack('CnA*', ::MENU_TEXT(), length($entry->Title()), $entry->Title()); - }; - }; - - }; - - -# -# Function: CheckForTrashedMenu -# -# Checks the menu to see if a significant number of file entries didn't resolve to actual files, and if so, saves a backup of the -# menu and issues a warning. -# -# Parameters: -# -# numberOriginallyInMenu - A count of how many file entries were in the menu orignally. -# numberRemoved - A count of how many file entries were removed from the menu. -# -sub CheckForTrashedMenu #(numberOriginallyInMenu, numberRemoved) - { - my ($self, $numberOriginallyInMenu, $numberRemoved) = @_; - - no integer; - - if ( ($numberOriginallyInMenu >= 6 && $numberRemoved == $numberOriginallyInMenu) || - ($numberOriginallyInMenu >= 12 && ($numberRemoved / $numberOriginallyInMenu) >= 0.4) || - ($numberRemoved >= 15) ) - { - my $backupFile = NaturalDocs::Project->MenuBackupFile(); - - NaturalDocs::File->Copy( NaturalDocs::Project->MenuFile(), $backupFile ); - - print STDERR - "\n" - # GNU format. See http://www.gnu.org/prep/standards_15.html - . "NaturalDocs: warning: possible trashed menu\n" - . "\n" - . " Natural Docs has detected that a significant number file entries in the\n" - . " menu did not resolve to actual files. A backup of your original menu file\n" - . " has been saved as\n" - . "\n" - . " " . $backupFile . "\n" - . "\n" - . " - If you recently deleted a lot of files from your project, you can safely\n" - . " ignore this message. They have been deleted from the menu as well.\n" - . " - If you recently rearranged your source tree, you may want to restore your\n" - . " menu from the backup and do a search and replace to preserve your layout.\n" - . " Otherwise the position of any moved files will be reset.\n" - . " - If neither of these is the case, you may have gotten the -i parameter\n" - . " wrong in the command line. You should definitely restore the backup and\n" - . " try again, because otherwise every file in your menu will be reset.\n" - . "\n"; - }; - - use integer; - }; - - -############################################################################### -# Group: Auto-Adjustment Functions - - -# -# Function: ResolveInputDirectories -# -# Detects if the input directories in the menu file match those in the command line, and if not, tries to resolve them. This allows -# menu files to work across machines, since the absolute paths won't be the same but the relative ones should be. -# -# Parameters: -# -# inputDirectoryNames - A hashref of the input directories appearing in the menu file, or undef if none. The keys are the -# directories, and the values are their names. May be undef. -# -sub ResolveInputDirectories #(inputDirectoryNames) - { - my ($self, $menuDirectoryNames) = @_; - - - # Determine which directories don't match the command line, if any. - - my $inputDirectories = NaturalDocs::Settings->InputDirectories(); - my @unresolvedMenuDirectories; - - foreach my $menuDirectory (keys %$menuDirectoryNames) - { - my $found; - - foreach my $inputDirectory (@$inputDirectories) - { - if ($menuDirectory eq $inputDirectory) - { - $found = 1; - last; - }; - }; - - if (!$found) - { push @unresolvedMenuDirectories, $menuDirectory; }; - }; - - # Quit if everything matches up, which should be the most common case. - if (!scalar @unresolvedMenuDirectories) - { return; }; - - # Poop. See which input directories are still available. - - my @unresolvedInputDirectories; - - foreach my $inputDirectory (@$inputDirectories) - { - if (!exists $menuDirectoryNames->{$inputDirectory}) - { push @unresolvedInputDirectories, $inputDirectory; }; - }; - - # Quit if there are none. This means an input directory is in the menu that isn't in the command line. Natural Docs should - # proceed normally and let the files be deleted. - if (!scalar @unresolvedInputDirectories) - { - $hasChanged = 1; - return; - }; - - # The index into menuDirectoryScores is the same as in unresolvedMenuDirectories. The index into each arrayref within it is - # the same as in unresolvedInputDirectories. - my @menuDirectoryScores; - for (my $i = 0; $i < scalar @unresolvedMenuDirectories; $i++) - { push @menuDirectoryScores, [ ]; }; - - - # Now plow through the menu, looking for files that have an unresolved base. - - my @menuGroups = ( $menu ); - - while (scalar @menuGroups) - { - my $currentGroup = pop @menuGroups; - my $currentGroupContent = $currentGroup->GroupContent(); - - foreach my $entry (@$currentGroupContent) - { - if ($entry->Type() == ::MENU_GROUP()) - { - push @menuGroups, $entry; - } - elsif ($entry->Type() == ::MENU_FILE()) - { - # Check if it uses an unresolved base. - for (my $i = 0; $i < scalar @unresolvedMenuDirectories; $i++) - { - if (NaturalDocs::File->IsSubPathOf($unresolvedMenuDirectories[$i], $entry->Target())) - { - my $relativePath = NaturalDocs::File->MakeRelativePath($unresolvedMenuDirectories[$i], $entry->Target()); - $self->ResolveFile($relativePath, \@unresolvedInputDirectories, $menuDirectoryScores[$i]); - last; - }; - }; - }; - }; - }; - - - # Now, create an array of score objects. Each score object is the three value arrayref [ from, to, score ]. From and To are the - # conversion options and are the indexes into unresolvedInput/MenuDirectories. We'll sort this array by score to get the best - # possible conversions. Yes, really. - my @scores; - - for (my $menuIndex = 0; $menuIndex < scalar @unresolvedMenuDirectories; $menuIndex++) - { - for (my $inputIndex = 0; $inputIndex < scalar @unresolvedInputDirectories; $inputIndex++) - { - if ($menuDirectoryScores[$menuIndex]->[$inputIndex]) - { - push @scores, [ $menuIndex, $inputIndex, $menuDirectoryScores[$menuIndex]->[$inputIndex] ]; - }; - }; - }; - - @scores = sort { $b->[2] <=> $a->[2] } @scores; - - - # Now we determine what goes where. - my @menuDirectoryConversions; - - foreach my $scoreObject (@scores) - { - if (!defined $menuDirectoryConversions[ $scoreObject->[0] ]) - { - $menuDirectoryConversions[ $scoreObject->[0] ] = $unresolvedInputDirectories[ $scoreObject->[1] ]; - }; - }; - - - # Now, FINALLY, we do the conversion. Note that not every menu directory may have a conversion defined. - - @menuGroups = ( $menu ); - - while (scalar @menuGroups) - { - my $currentGroup = pop @menuGroups; - my $currentGroupContent = $currentGroup->GroupContent(); - - foreach my $entry (@$currentGroupContent) - { - if ($entry->Type() == ::MENU_GROUP()) - { - push @menuGroups, $entry; - } - elsif ($entry->Type() == ::MENU_FILE()) - { - # Check if it uses an unresolved base. - for (my $i = 0; $i < scalar @unresolvedMenuDirectories; $i++) - { - if (NaturalDocs::File->IsSubPathOf($unresolvedMenuDirectories[$i], $entry->Target()) && - defined $menuDirectoryConversions[$i]) - { - my $relativePath = NaturalDocs::File->MakeRelativePath($unresolvedMenuDirectories[$i], $entry->Target()); - $entry->SetTarget( NaturalDocs::File->JoinPaths($menuDirectoryConversions[$i], $relativePath) ); - last; - }; - }; - }; - }; - }; - - - # Whew. - - $hasChanged = 1; - }; - - -# -# Function: ResolveRelativeInputDirectories -# -# Resolves relative input directories to the input directories available. -# -sub ResolveRelativeInputDirectories - { - my ($self) = @_; - - my $inputDirectories = NaturalDocs::Settings->InputDirectories(); - my $resolvedInputDirectory; - - if (scalar @$inputDirectories == 1) - { $resolvedInputDirectory = $inputDirectories->[0]; } - else - { - my @score; - - # Plow through the menu, looking for files and scoring them. - - my @menuGroups = ( $menu ); - - while (scalar @menuGroups) - { - my $currentGroup = pop @menuGroups; - my $currentGroupContent = $currentGroup->GroupContent(); - - foreach my $entry (@$currentGroupContent) - { - if ($entry->Type() == ::MENU_GROUP()) - { - push @menuGroups, $entry; - } - elsif ($entry->Type() == ::MENU_FILE()) - { - $self->ResolveFile($entry->Target(), $inputDirectories, \@score); - }; - }; - }; - - # Determine the best match. - - my $bestScore = 0; - my $bestIndex = 0; - - for (my $i = 0; $i < scalar @$inputDirectories; $i++) - { - if ($score[$i] > $bestScore) - { - $bestScore = $score[$i]; - $bestIndex = $i; - }; - }; - - $resolvedInputDirectory = $inputDirectories->[$bestIndex]; - }; - - - # Okay, now that we have our resolved directory, update everything. - - my @menuGroups = ( $menu ); - - while (scalar @menuGroups) - { - my $currentGroup = pop @menuGroups; - my $currentGroupContent = $currentGroup->GroupContent(); - - foreach my $entry (@$currentGroupContent) - { - if ($entry->Type() == ::MENU_GROUP()) - { push @menuGroups, $entry; } - elsif ($entry->Type() == ::MENU_FILE()) - { - $entry->SetTarget( NaturalDocs::File->JoinPaths($resolvedInputDirectory, $entry->Target()) ); - }; - }; - }; - - if (scalar @$inputDirectories > 1) - { $hasChanged = 1; }; - - return $resolvedInputDirectory; - }; - - -# -# Function: ResolveFile -# -# Tests a relative path against a list of directories. Adds one to the score of each base where there is a match. -# -# Parameters: -# -# relativePath - The relative file name to test. -# possibleBases - An arrayref of bases to test it against. -# possibleBaseScores - An arrayref of scores to adjust. The score indexes should correspond to the base indexes. -# -sub ResolveFile #(relativePath, possibleBases, possibleBaseScores) - { - my ($self, $relativePath, $possibleBases, $possibleBaseScores) = @_; - - for (my $i = 0; $i < scalar @$possibleBases; $i++) - { - if (-e NaturalDocs::File->JoinPaths($possibleBases->[$i], $relativePath)) - { $possibleBaseScores->[$i]++; }; - }; - }; - - -# -# Function: LockUserTitleChanges -# -# Detects if the user manually changed any file titles, and if so, automatically locks them with <MENU_FILE_NOAUTOTITLE>. -# -# Parameters: -# -# previousMenuFiles - A hashref of the files from the previous menu state. The keys are the <FileNames>, and the values are -# references to their <NaturalDocs::Menu::Entry> objects. -# -sub LockUserTitleChanges #(previousMenuFiles) - { - my ($self, $previousMenuFiles) = @_; - - my @groupStack = ( $menu ); - my $groupEntry; - - while (scalar @groupStack) - { - $groupEntry = pop @groupStack; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - - # If it's an unlocked file entry - if ($entry->Type() == ::MENU_FILE() && ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0) - { - my $previousEntry = $previousMenuFiles->{$entry->Target()}; - - # If the previous entry was also unlocked and the titles are different, the user changed the title. Automatically lock it. - if (defined $previousEntry && ($previousEntry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0 && - $entry->Title() ne $previousEntry->Title()) - { - $entry->SetFlags($entry->Flags() | ::MENU_FILE_NOAUTOTITLE()); - $hasChanged = 1; - }; - } - - elsif ($entry->Type() == ::MENU_GROUP()) - { - push @groupStack, $entry; - }; - - }; - }; - }; - - -# -# Function: FlagAutoTitleChanges -# -# Finds which files have auto-titles that changed and flags their groups for updating with <MENU_GROUP_UPDATETITLES> and -# <MENU_GROUP_UPDATEORDER>. -# -sub FlagAutoTitleChanges - { - my ($self) = @_; - - my @groupStack = ( $menu ); - my $groupEntry; - - while (scalar @groupStack) - { - $groupEntry = pop @groupStack; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_FILE() && ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0 && - exists $defaultTitlesChanged{$entry->Target()}) - { - $groupEntry->SetFlags($groupEntry->Flags() | ::MENU_GROUP_UPDATETITLES() | ::MENU_GROUP_UPDATEORDER()); - $hasChanged = 1; - } - elsif ($entry->Type() == ::MENU_GROUP()) - { - push @groupStack, $entry; - }; - }; - }; - }; - - -# -# Function: AutoPlaceNewFiles -# -# Adds files to the menu that aren't already on it, attempting to guess where they belong. -# -# New files are placed after a dummy <MENU_ENDOFORIGINAL> entry so that they don't affect the detected order. Also, the -# groups they're placed in get <MENU_GROUP_UPDATETITLES>, <MENU_GROUP_UPDATESTRUCTURE>, and -# <MENU_GROUP_UPDATEORDER> flags. -# -# Parameters: -# -# filesInMenu - An existence hash of all the <FileNames> present in the menu. -# -sub AutoPlaceNewFiles #(fileInMenu) - { - my ($self, $filesInMenu) = @_; - - my $files = NaturalDocs::Project->FilesWithContent(); - - my $directories; - - foreach my $file (keys %$files) - { - if (!exists $filesInMenu->{$file}) - { - # This is done on demand because new files shouldn't be added very often, so this will save time. - if (!defined $directories) - { $directories = $self->MatchDirectoriesAndGroups(); }; - - my $targetGroup; - my $fileDirectoryString = (NaturalDocs::File->SplitPath($file))[1]; - - $targetGroup = $directories->{$fileDirectoryString}; - - if (!defined $targetGroup) - { - # Okay, if there's no exact match, work our way down. - - my @fileDirectories = NaturalDocs::File->SplitDirectories($fileDirectoryString); - - do - { - pop @fileDirectories; - $targetGroup = $directories->{ NaturalDocs::File->JoinDirectories(@fileDirectories) }; - } - while (!defined $targetGroup && scalar @fileDirectories); - - if (!defined $targetGroup) - { $targetGroup = $menu; }; - }; - - $targetGroup->MarkEndOfOriginal(); - $targetGroup->PushToGroup( NaturalDocs::Menu::Entry->New(::MENU_FILE(), undef, $file, undef) ); - - $targetGroup->SetFlags( $targetGroup->Flags() | ::MENU_GROUP_UPDATETITLES() | - ::MENU_GROUP_UPDATESTRUCTURE() | ::MENU_GROUP_UPDATEORDER() ); - - $hasChanged = 1; - }; - }; - }; - - -# -# Function: MatchDirectoriesAndGroups -# -# Determines which groups files in certain directories should be placed in. -# -# Returns: -# -# A hashref. The keys are the directory names, and the values are references to the group objects they should be placed in. -# -# This only repreesents directories that currently have files on the menu, so it shouldn't be assumed that every possible -# directory will exist. To match, you should first try to match the directory, and then strip the deepest directories one by -# one until there's a match or there's none left. If there's none left, use the root group <menu>. -# -sub MatchDirectoriesAndGroups - { - my ($self) = @_; - - # The keys are the directory names, and the values are hashrefs. For the hashrefs, the keys are the group objects, and the - # values are the number of files in them from that directory. In other words, - # $directories{$directory}->{$groupEntry} = $count; - my %directories; - # Note that we need to use Tie::RefHash to use references as keys. Won't work otherwise. Also, not every Perl distro comes - # with Tie::RefHash::Nestable, so we can't rely on that. - - # We're using an index instead of pushing and popping because we want to save a list of the groups in the order they appear - # to break ties. - my @groups = ( $menu ); - my $groupIndex = 0; - - - # Count the number of files in each group that appear in each directory. - - while ($groupIndex < scalar @groups) - { - my $groupEntry = $groups[$groupIndex]; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_GROUP()) - { - push @groups, $entry; - } - elsif ($entry->Type() == ::MENU_FILE()) - { - my $directory = (NaturalDocs::File->SplitPath($entry->Target()))[1]; - - if (!exists $directories{$directory}) - { - my $subHash = { }; - tie %$subHash, 'Tie::RefHash'; - $directories{$directory} = $subHash; - }; - - if (!exists $directories{$directory}->{$groupEntry}) - { $directories{$directory}->{$groupEntry} = 1; } - else - { $directories{$directory}->{$groupEntry}++; }; - }; - }; - - $groupIndex++; - }; - - - # Determine which group goes with which directory, breaking ties by using whichever group appears first. - - my $finalDirectories = { }; - - while (my ($directory, $directoryGroups) = each %directories) - { - my $bestGroup; - my $bestCount = 0; - my %tiedGroups; # Existence hash - - while (my ($group, $count) = each %$directoryGroups) - { - if ($count > $bestCount) - { - $bestGroup = $group; - $bestCount = $count; - %tiedGroups = ( ); - } - elsif ($count == $bestCount) - { - $tiedGroups{$group} = 1; - }; - }; - - # Break ties. - if (scalar keys %tiedGroups) - { - $tiedGroups{$bestGroup} = 1; - - foreach my $group (@groups) - { - if (exists $tiedGroups{$group}) - { - $bestGroup = $group; - last; - }; - }; - }; - - - $finalDirectories->{$directory} = $bestGroup; - }; - - - return $finalDirectories; - }; - - -# -# Function: RemoveDeadFiles -# -# Removes files from the menu that no longer exist or no longer have Natural Docs content. -# -# Returns: -# -# The number of file entries removed. -# -sub RemoveDeadFiles - { - my ($self) = @_; - - my @groupStack = ( $menu ); - my $numberRemoved = 0; - - my $filesWithContent = NaturalDocs::Project->FilesWithContent(); - - while (scalar @groupStack) - { - my $groupEntry = pop @groupStack; - my $groupContent = $groupEntry->GroupContent(); - - my $index = 0; - while ($index < scalar @$groupContent) - { - if ($groupContent->[$index]->Type() == ::MENU_FILE() && - !exists $filesWithContent->{ $groupContent->[$index]->Target() } ) - { - $groupEntry->DeleteFromGroup($index); - - $groupEntry->SetFlags( $groupEntry->Flags() | ::MENU_GROUP_UPDATETITLES() | - ::MENU_GROUP_UPDATESTRUCTURE() ); - $numberRemoved++; - $hasChanged = 1; - } - - elsif ($groupContent->[$index]->Type() == ::MENU_GROUP()) - { - push @groupStack, $groupContent->[$index]; - $index++; - } - - else - { $index++; }; - }; - }; - - return $numberRemoved; - }; - - -# -# Function: BanAndUnbanIndexes -# -# Adjusts the indexes that are banned depending on if the user added or deleted any. -# -sub BanAndUnbanIndexes - { - my ($self) = @_; - - # Unban any indexes that are present, meaning the user added them back manually without deleting the ban. - foreach my $index (keys %indexes) - { delete $bannedIndexes{$index}; }; - - # Ban any indexes that were in the previous menu but not the current, meaning the user manually deleted them. However, - # don't do this if the topic isn't indexable, meaning they changed the topic type rather than the menu. - foreach my $index (keys %previousIndexes) - { - if (!exists $indexes{$index} && NaturalDocs::Topics->TypeInfo($index)->Index()) - { $bannedIndexes{$index} = 1; }; - }; - }; - - -# -# Function: AddAndRemoveIndexes -# -# Automatically adds and removes index entries on the menu as necessary. <DetectIndexGroups()> should be called -# beforehand. -# -sub AddAndRemoveIndexes - { - my ($self) = @_; - - my %validIndexes; - my @allIndexes = NaturalDocs::Topics->AllIndexableTypes(); - - foreach my $index (@allIndexes) - { - # Strip the banned indexes first so it's potentially less work for SymbolTable. - if (!exists $bannedIndexes{$index}) - { $validIndexes{$index} = 1; }; - }; - - %validIndexes = %{NaturalDocs::SymbolTable->HasIndexes(\%validIndexes)}; - - - # Delete dead indexes and find the best index group. - - my @groupStack = ( $menu ); - - my $bestIndexGroup; - my $bestIndexCount = 0; - - while (scalar @groupStack) - { - my $currentGroup = pop @groupStack; - my $index = 0; - - my $currentIndexCount = 0; - - while ($index < scalar @{$currentGroup->GroupContent()}) - { - my $entry = $currentGroup->GroupContent()->[$index]; - - if ($entry->Type() == ::MENU_INDEX()) - { - $currentIndexCount++; - - if ($currentIndexCount > $bestIndexCount) - { - $bestIndexCount = $currentIndexCount; - $bestIndexGroup = $currentGroup; - }; - - # Remove it if it's dead. - - if (!exists $validIndexes{ $entry->Target() }) - { - $currentGroup->DeleteFromGroup($index); - delete $indexes{ $entry->Target() }; - $hasChanged = 1; - } - else - { $index++; }; - } - - else - { - if ($entry->Type() == ::MENU_GROUP()) - { push @groupStack, $entry; }; - - $index++; - }; - }; - }; - - - # Now add the new indexes. - - foreach my $index (keys %indexes) - { delete $validIndexes{$index}; }; - - if (scalar keys %validIndexes) - { - # Add a group if there are no indexes at all. - - if ($bestIndexCount == 0) - { - $menu->MarkEndOfOriginal(); - - my $newIndexGroup = NaturalDocs::Menu::Entry->New(::MENU_GROUP(), 'Index', undef, - ::MENU_GROUP_ISINDEXGROUP()); - $menu->PushToGroup($newIndexGroup); - - $bestIndexGroup = $newIndexGroup; - $menu->SetFlags( $menu->Flags() | ::MENU_GROUP_UPDATEORDER() | ::MENU_GROUP_UPDATESTRUCTURE() ); - }; - - # Add the new indexes. - - $bestIndexGroup->MarkEndOfOriginal(); - my $isIndexGroup = $bestIndexGroup->Flags() & ::MENU_GROUP_ISINDEXGROUP(); - - foreach my $index (keys %validIndexes) - { - my $title; - - if ($isIndexGroup) - { - if ($index eq ::TOPIC_GENERAL()) - { $title = 'Everything'; } - else - { $title = NaturalDocs::Topics->NameOfType($index, 1); }; - } - else - { - $title = NaturalDocs::Topics->NameOfType($index) . ' Index'; - }; - - my $newEntry = NaturalDocs::Menu::Entry->New(::MENU_INDEX(), $title, $index, undef); - $bestIndexGroup->PushToGroup($newEntry); - - $indexes{$index} = 1; - }; - - $bestIndexGroup->SetFlags( $bestIndexGroup->Flags() | - ::MENU_GROUP_UPDATEORDER() | ::MENU_GROUP_UPDATESTRUCTURE() ); - $hasChanged = 1; - }; - }; - - -# -# Function: RemoveDeadGroups -# -# Removes groups with less than two entries. It will always remove empty groups, and it will remove groups with one entry if it -# has the <MENU_GROUP_UPDATESTRUCTURE> flag. -# -sub RemoveDeadGroups - { - my ($self) = @_; - - my $index = 0; - - while ($index < scalar @{$menu->GroupContent()}) - { - my $entry = $menu->GroupContent()->[$index]; - - if ($entry->Type() == ::MENU_GROUP()) - { - my $removed = $self->RemoveIfDead($entry, $menu, $index); - - if (!$removed) - { $index++; }; - } - else - { $index++; }; - }; - }; - - -# -# Function: RemoveIfDead -# -# Checks a group and all its sub-groups for life and remove any that are dead. Empty groups are removed, and groups with one -# entry and the <MENU_GROUP_UPDATESTRUCTURE> flag have their entry moved to the parent group. -# -# Parameters: -# -# groupEntry - The group to check for possible deletion. -# parentGroupEntry - The parent group to move the single entry to if necessary. -# parentGroupIndex - The index of the group in its parent. -# -# Returns: -# -# Whether the group was removed or not. -# -sub RemoveIfDead #(groupEntry, parentGroupEntry, parentGroupIndex) - { - my ($self, $groupEntry, $parentGroupEntry, $parentGroupIndex) = @_; - - - # Do all sub-groups first, since their deletions will affect our UPDATESTRUCTURE flag and content count. - - my $index = 0; - while ($index < scalar @{$groupEntry->GroupContent()}) - { - my $entry = $groupEntry->GroupContent()->[$index]; - - if ($entry->Type() == ::MENU_GROUP()) - { - my $removed = $self->RemoveIfDead($entry, $groupEntry, $index); - - if (!$removed) - { $index++; }; - } - else - { $index++; }; - }; - - - # Now check ourself. - - my $count = scalar @{$groupEntry->GroupContent()}; - if ($groupEntry->Flags() & ::MENU_GROUP_HASENDOFORIGINAL()) - { $count--; }; - - if ($count == 0) - { - $parentGroupEntry->DeleteFromGroup($parentGroupIndex); - - $parentGroupEntry->SetFlags( $parentGroupEntry->Flags() | ::MENU_GROUP_UPDATESTRUCTURE() ); - - $hasChanged = 1; - return 1; - } - elsif ($count == 1 && ($groupEntry->Flags() & ::MENU_GROUP_UPDATESTRUCTURE()) ) - { - my $onlyEntry = $groupEntry->GroupContent()->[0]; - if ($onlyEntry->Type() == ::MENU_ENDOFORIGINAL()) - { $onlyEntry = $groupEntry->GroupContent()->[1]; }; - - $parentGroupEntry->DeleteFromGroup($parentGroupIndex); - - $parentGroupEntry->MarkEndOfOriginal(); - $parentGroupEntry->PushToGroup($onlyEntry); - - $parentGroupEntry->SetFlags( $parentGroupEntry->Flags() | ::MENU_GROUP_UPDATETITLES() | - ::MENU_GROUP_UPDATEORDER() | ::MENU_GROUP_UPDATESTRUCTURE() ); - - $hasChanged = 1; - return 1; - } - else - { return undef; }; - }; - - -# -# Function: DetectIndexGroups -# -# Finds groups that are primarily used for indexes and gives them the <MENU_GROUP_ISINDEXGROUP> flag. -# -sub DetectIndexGroups - { - my ($self) = @_; - - my @groupStack = ( $menu ); - - while (scalar @groupStack) - { - my $groupEntry = pop @groupStack; - - my $isIndexGroup = -1; # -1: Can't tell yet. 0: Can't be an index group. 1: Is an index group so far. - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_INDEX()) - { - if ($isIndexGroup == -1) - { $isIndexGroup = 1; }; - } - - # Text is tolerated, but it still needs at least one index entry. - elsif ($entry->Type() != ::MENU_TEXT()) - { - $isIndexGroup = 0; - - if ($entry->Type() == ::MENU_GROUP()) - { push @groupStack, $entry; }; - }; - }; - - if ($isIndexGroup == 1) - { - $groupEntry->SetFlags( $groupEntry->Flags() | ::MENU_GROUP_ISINDEXGROUP() ); - }; - }; - }; - - -# -# Function: CreateDirectorySubGroups -# -# Where possible, creates sub-groups based on directories for any long groups that have <MENU_GROUP_UPDATESTRUCTURE> -# set. Clears the flag afterwards on groups that are short enough to not need any more sub-groups, but leaves it for the rest. -# -sub CreateDirectorySubGroups - { - my ($self) = @_; - - my @groupStack = ( $menu ); - - foreach my $groupEntry (@groupStack) - { - if ($groupEntry->Flags() & ::MENU_GROUP_UPDATESTRUCTURE()) - { - # Count the number of files. - - my $fileCount = 0; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_FILE()) - { $fileCount++; }; - }; - - - if ($fileCount > MAXFILESINGROUP) - { - my @sharedDirectories = $self->SharedDirectoriesOf($groupEntry); - my $unsharedIndex = scalar @sharedDirectories; - - # The keys are the first directory entries after the shared ones, and the values are the number of files that are in - # that directory. Files that don't have subdirectories after the shared directories aren't included because they shouldn't - # be put in a subgroup. - my %directoryCounts; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_FILE()) - { - my @entryDirectories = NaturalDocs::File->SplitDirectories( (NaturalDocs::File->SplitPath($entry->Target()))[1] ); - - if (scalar @entryDirectories > $unsharedIndex) - { - my $unsharedDirectory = $entryDirectories[$unsharedIndex]; - - if (!exists $directoryCounts{$unsharedDirectory}) - { $directoryCounts{$unsharedDirectory} = 1; } - else - { $directoryCounts{$unsharedDirectory}++; }; - }; - }; - }; - - - # Now create the subgroups. - - # The keys are the first directory entries after the shared ones, and the values are the groups for those files to be - # put in. There will only be entries for the groups with at least MINFILESINNEWGROUP files. - my %directoryGroups; - - while (my ($directory, $count) = each %directoryCounts) - { - if ($count >= MINFILESINNEWGROUP) - { - my $newGroup = NaturalDocs::Menu::Entry->New( ::MENU_GROUP(), ucfirst($directory), undef, - ::MENU_GROUP_UPDATETITLES() | - ::MENU_GROUP_UPDATEORDER() ); - - if ($count > MAXFILESINGROUP) - { $newGroup->SetFlags( $newGroup->Flags() | ::MENU_GROUP_UPDATESTRUCTURE()); }; - - $groupEntry->MarkEndOfOriginal(); - push @{$groupEntry->GroupContent()}, $newGroup; - - $directoryGroups{$directory} = $newGroup; - $fileCount -= $count; - }; - }; - - - # Now fill the subgroups. - - if (scalar keys %directoryGroups) - { - my $afterOriginal; - my $index = 0; - - while ($index < scalar @{$groupEntry->GroupContent()}) - { - my $entry = $groupEntry->GroupContent()->[$index]; - - if ($entry->Type() == ::MENU_FILE()) - { - my @entryDirectories = - NaturalDocs::File->SplitDirectories( (NaturalDocs::File->SplitPath($entry->Target()))[1] ); - - my $unsharedDirectory = $entryDirectories[$unsharedIndex]; - - if (exists $directoryGroups{$unsharedDirectory}) - { - my $targetGroup = $directoryGroups{$unsharedDirectory}; - - if ($afterOriginal) - { $targetGroup->MarkEndOfOriginal(); }; - $targetGroup->PushToGroup($entry); - - $groupEntry->DeleteFromGroup($index); - } - else - { $index++; }; - } - - elsif ($entry->Type() == ::MENU_ENDOFORIGINAL()) - { - $afterOriginal = 1; - $index++; - } - - elsif ($entry->Type() == ::MENU_GROUP()) - { - # See if we need to relocate this group. - - my @groupDirectories = $self->SharedDirectoriesOf($entry); - - # The group's shared directories must be at least two levels deeper than the current. If the first level deeper - # is a new group, move it there because it's a subdirectory of that one. - if (scalar @groupDirectories - scalar @sharedDirectories >= 2) - { - my $unsharedDirectory = $groupDirectories[$unsharedIndex]; - - if (exists $directoryGroups{$unsharedDirectory} && - $directoryGroups{$unsharedDirectory} != $entry) - { - my $targetGroup = $directoryGroups{$unsharedDirectory}; - - if ($afterOriginal) - { $targetGroup->MarkEndOfOriginal(); }; - $targetGroup->PushToGroup($entry); - - $groupEntry->DeleteFromGroup($index); - - # We need to retitle the group if it has the name of the unshared directory. - - my $oldTitle = $entry->Title(); - $oldTitle =~ s/ +//g; - $unsharedDirectory =~ s/ +//g; - - if (lc($oldTitle) eq lc($unsharedDirectory)) - { - $entry->SetTitle($groupDirectories[$unsharedIndex + 1]); - }; - } - else - { $index++; }; - } - else - { $index++; }; - } - - else - { $index++; }; - }; - - $hasChanged = 1; - - if ($fileCount <= MAXFILESINGROUP) - { $groupEntry->SetFlags( $groupEntry->Flags() & ~::MENU_GROUP_UPDATESTRUCTURE() ); }; - - $groupEntry->SetFlags( $groupEntry->Flags() | ::MENU_GROUP_UPDATETITLES() | - ::MENU_GROUP_UPDATEORDER() ); - }; - - }; # If group has >MAXFILESINGROUP files - }; # If group has UPDATESTRUCTURE - - - # Okay, now go through all the subgroups. We do this after the above so that newly created groups can get subgrouped - # further. - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_GROUP()) - { push @groupStack, $entry; }; - }; - - }; # For each group entry - }; - - -# -# Function: DetectOrder -# -# Detects the order of the entries in all groups that have the <MENU_GROUP_UPDATEORDER> flag set. Will set one of the -# <MENU_GROUP_FILESSORTED>, <MENU_GROUP_FILESANDGROUPSSORTED>, <MENU_GROUP_EVERYTHINGSORTED>, or -# <MENU_GROUP_UNSORTED> flags. It will always go for the most comprehensive sort possible, so if a group only has one -# entry, it will be flagged as <MENU_GROUP_EVERYTHINGSORTED>. -# -# <DetectIndexGroups()> should be called beforehand, as the <MENU_GROUP_ISINDEXGROUP> flag affects how the order is -# detected. -# -# The sort detection stops if it reaches a <MENU_ENDOFORIGINAL> entry, so new entries can be added to the end while still -# allowing the original sort to be detected. -# -# Parameters: -# -# forceAll - If set, the order will be detected for all groups regardless of whether <MENU_GROUP_UPDATEORDER> is set. -# -sub DetectOrder #(forceAll) - { - my ($self, $forceAll) = @_; - my @groupStack = ( $menu ); - - while (scalar @groupStack) - { - my $groupEntry = pop @groupStack; - my $index = 0; - - - # First detect the sort. - - if ($forceAll || ($groupEntry->Flags() & ::MENU_GROUP_UPDATEORDER()) ) - { - my $order = ::MENU_GROUP_EVERYTHINGSORTED(); - - my $lastFile; - my $lastFileOrGroup; - - while ($index < scalar @{$groupEntry->GroupContent()} && - $groupEntry->GroupContent()->[$index]->Type() != ::MENU_ENDOFORIGINAL() && - $order != ::MENU_GROUP_UNSORTED()) - { - my $entry = $groupEntry->GroupContent()->[$index]; - - - # Ignore the last entry if it's an index group. We don't want it to affect the sort. - - if ($index + 1 == scalar @{$groupEntry->GroupContent()} && - $entry->Type() == ::MENU_GROUP() && ($entry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) ) - { - # Ignore. - - # This is an awkward code construct, basically working towards an else instead of using an if, but the code just gets - # too hard to read otherwise. The compiled code should work out to roughly the same thing anyway. - } - - - # Ignore the first entry if it's the general index in an index group. We don't want it to affect the sort. - - elsif ($index == 0 && ($groupEntry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) && - $entry->Type() == ::MENU_INDEX() && $entry->Target() eq ::TOPIC_GENERAL() ) - { - # Ignore. - } - - - # Degenerate the sort. - - else - { - - if ($order == ::MENU_GROUP_EVERYTHINGSORTED() && $index > 0 && - ::StringCompare($entry->Title(), $groupEntry->GroupContent()->[$index - 1]->Title()) < 0) - { $order = ::MENU_GROUP_FILESANDGROUPSSORTED(); }; - - if ($order == ::MENU_GROUP_FILESANDGROUPSSORTED() && - ($entry->Type() == ::MENU_FILE() || $entry->Type() == ::MENU_GROUP()) && - defined $lastFileOrGroup && ::StringCompare($entry->Title(), $lastFileOrGroup->Title()) < 0) - { $order = ::MENU_GROUP_FILESSORTED(); }; - - if ($order == ::MENU_GROUP_FILESSORTED() && - $entry->Type() == ::MENU_FILE() && defined $lastFile && - ::StringCompare($entry->Title(), $lastFile->Title()) < 0) - { $order = ::MENU_GROUP_UNSORTED(); }; - - }; - - - # Set the lastX parameters for comparison and add sub-groups to the stack. - - if ($entry->Type() == ::MENU_FILE()) - { - $lastFile = $entry; - $lastFileOrGroup = $entry; - } - elsif ($entry->Type() == ::MENU_GROUP()) - { - $lastFileOrGroup = $entry; - push @groupStack, $entry; - }; - - $index++; - }; - - $groupEntry->SetFlags($groupEntry->Flags() | $order); - }; - - - # Find any subgroups in the remaining entries. - - while ($index < scalar @{$groupEntry->GroupContent()}) - { - my $entry = $groupEntry->GroupContent()->[$index]; - - if ($entry->Type() == ::MENU_GROUP()) - { push @groupStack, $entry; }; - - $index++; - }; - }; - }; - - -# -# Function: GenerateAutoFileTitles -# -# Creates titles for the unlocked file entries in all groups that have the <MENU_GROUP_UPDATETITLES> flag set. It clears the -# flag afterwards so it can be used efficiently for multiple sweeps. -# -# Parameters: -# -# forceAll - If set, forces all the unlocked file titles to update regardless of whether the group has the -# <MENU_GROUP_UPDATETITLES> flag set. -# -sub GenerateAutoFileTitles #(forceAll) - { - my ($self, $forceAll) = @_; - - my @groupStack = ( $menu ); - - while (scalar @groupStack) - { - my $groupEntry = pop @groupStack; - - if ($forceAll || ($groupEntry->Flags() & ::MENU_GROUP_UPDATETITLES()) ) - { - # Find common prefixes and paths to strip from the default menu titles. - - my @sharedDirectories; - my $noSharedDirectories; - - my @sharedPrefixes; - my $noSharedPrefixes; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_FILE()) - { - # Find the common path among all file entries in this group. - - if (!$noSharedDirectories) - { - my ($volume, $directoryString, $file) = NaturalDocs::File->SplitPath($entry->Target()); - my @entryDirectories = NaturalDocs::File->SplitDirectories($directoryString); - - if (!scalar @entryDirectories) - { $noSharedDirectories = 1; } - elsif (!scalar @sharedDirectories) - { @sharedDirectories = @entryDirectories; } - elsif ($entryDirectories[0] ne $sharedDirectories[0]) - { $noSharedDirectories = 1; } - - # If both arrays have entries, and the first is shared... - else - { - my $index = 1; - - while ($index < scalar @sharedDirectories && $index < scalar @entryDirectories && - $entryDirectories[$index] eq $sharedDirectories[$index]) - { $index++; }; - - if ($index < scalar @sharedDirectories) - { splice(@sharedDirectories, $index); }; - }; - }; - - - # Find the common prefixes among all file entries that are unlocked and don't use the file name as their default title. - - if (!$noSharedPrefixes && ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0 && - NaturalDocs::Project->DefaultMenuTitleOf($entry->Target()) ne $entry->Target()) - { - my @entryPrefixes = split(/(\.|::|->)/, NaturalDocs::Project->DefaultMenuTitleOf($entry->Target())); - - # Remove potential leading undef/empty string. - if (!length $entryPrefixes[0]) - { shift @entryPrefixes; }; - - # Remove last entry. Something has to exist for the title. - pop @entryPrefixes; - - if (!scalar @entryPrefixes) - { $noSharedPrefixes = 1; } - elsif (!scalar @sharedPrefixes) - { @sharedPrefixes = @entryPrefixes; } - elsif ($entryPrefixes[0] ne $sharedPrefixes[0]) - { $noSharedPrefixes = 1; } - - # If both arrays have entries, and the first is shared... - else - { - my $index = 1; - - while ($index < scalar @sharedPrefixes && $entryPrefixes[$index] eq $sharedPrefixes[$index]) - { $index++; }; - - if ($index < scalar @sharedPrefixes) - { splice(@sharedPrefixes, $index); }; - }; - }; - - }; # if entry is MENU_FILE - }; # foreach entry in group content. - - - if (!scalar @sharedDirectories) - { $noSharedDirectories = 1; }; - if (!scalar @sharedPrefixes) - { $noSharedPrefixes = 1; }; - - - # Update all the menu titles of unlocked file entries. - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_FILE() && ($entry->Flags() & ::MENU_FILE_NOAUTOTITLE()) == 0) - { - my $title = NaturalDocs::Project->DefaultMenuTitleOf($entry->Target()); - - if ($title eq $entry->Target()) - { - my ($volume, $directoryString, $file) = NaturalDocs::File->SplitPath($title); - my @directories = NaturalDocs::File->SplitDirectories($directoryString); - - if (!$noSharedDirectories) - { splice(@directories, 0, scalar @sharedDirectories); }; - - # directory\...\directory\file.ext - - if (scalar @directories > 2) - { @directories = ( $directories[0], '...', $directories[-1] ); }; - - $directoryString = NaturalDocs::File->JoinDirectories(@directories); - $title = NaturalDocs::File->JoinPaths($directoryString, $file); - } - else - { - my @segments = split(/(::|\.|->)/, $title); - if (!length $segments[0]) - { shift @segments; }; - - if (!$noSharedPrefixes) - { splice(@segments, 0, scalar @sharedPrefixes); }; - - # package...package::target - - if (scalar @segments > 5) - { splice(@segments, 1, scalar @segments - 4, '...'); }; - - $title = join('', @segments); - }; - - $entry->SetTitle($title); - }; # If entry is an unlocked file - }; # Foreach entry - - $groupEntry->SetFlags( $groupEntry->Flags() & ~::MENU_GROUP_UPDATETITLES() ); - - }; # If updating group titles - - # Now find any subgroups. - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_GROUP()) - { push @groupStack, $entry; }; - }; - }; - - }; - - -# -# Function: ResortGroups -# -# Resorts all groups that have <MENU_GROUP_UPDATEORDER> set. Assumes <DetectOrder()> and <GenerateAutoFileTitles()> -# have already been called. Will clear the flag and any <MENU_ENDOFORIGINAL> entries on reordered groups. -# -# Parameters: -# -# forceAll - If set, resorts all groups regardless of whether <MENU_GROUP_UPDATEORDER> is set. -# -sub ResortGroups #(forceAll) - { - my ($self, $forceAll) = @_; - my @groupStack = ( $menu ); - - while (scalar @groupStack) - { - my $groupEntry = pop @groupStack; - - if ($forceAll || ($groupEntry->Flags() & ::MENU_GROUP_UPDATEORDER()) ) - { - my $newEntriesIndex; - - - # Strip the ENDOFORIGINAL. - - if ($groupEntry->Flags() & ::MENU_GROUP_HASENDOFORIGINAL()) - { - $newEntriesIndex = 0; - - while ($newEntriesIndex < scalar @{$groupEntry->GroupContent()} && - $groupEntry->GroupContent()->[$newEntriesIndex]->Type() != ::MENU_ENDOFORIGINAL() ) - { $newEntriesIndex++; }; - - $groupEntry->DeleteFromGroup($newEntriesIndex); - - $groupEntry->SetFlags( $groupEntry->Flags() & ~::MENU_GROUP_HASENDOFORIGINAL() ); - } - else - { $newEntriesIndex = -1; }; - - - # Strip the exceptions. - - my $trailingIndexGroup; - my $leadingGeneralIndex; - - if ( ($groupEntry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) && - $groupEntry->GroupContent()->[0]->Type() == ::MENU_INDEX() && - $groupEntry->GroupContent()->[0]->Target() eq ::TOPIC_GENERAL() ) - { - $leadingGeneralIndex = shift @{$groupEntry->GroupContent()}; - if ($newEntriesIndex != -1) - { $newEntriesIndex--; }; - } - - elsif (scalar @{$groupEntry->GroupContent()} && $newEntriesIndex != 0) - { - my $lastIndex; - - if ($newEntriesIndex != -1) - { $lastIndex = $newEntriesIndex - 1; } - else - { $lastIndex = scalar @{$groupEntry->GroupContent()} - 1; }; - - if ($groupEntry->GroupContent()->[$lastIndex]->Type() == ::MENU_GROUP() && - ( $groupEntry->GroupContent()->[$lastIndex]->Flags() & ::MENU_GROUP_ISINDEXGROUP() ) ) - { - $trailingIndexGroup = $groupEntry->GroupContent()->[$lastIndex]; - $groupEntry->DeleteFromGroup($lastIndex); - - if ($newEntriesIndex != -1) - { $newEntriesIndex++; }; - }; - }; - - - # If there weren't already exceptions, strip them from the new entries. - - if ( (!defined $trailingIndexGroup || !defined $leadingGeneralIndex) && $newEntriesIndex != -1) - { - my $index = $newEntriesIndex; - - while ($index < scalar @{$groupEntry->GroupContent()}) - { - my $entry = $groupEntry->GroupContent()->[$index]; - - if (!defined $trailingIndexGroup && - $entry->Type() == ::MENU_GROUP() && ($entry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) ) - { - $trailingIndexGroup = $entry; - $groupEntry->DeleteFromGroup($index); - } - elsif (!defined $leadingGeneralIndex && ($groupEntry->Flags() & ::MENU_GROUP_ISINDEXGROUP()) && - $entry->Type() == ::MENU_INDEX() && !defined $entry->Target()) - { - $leadingGeneralIndex = $entry; - $groupEntry->DeleteFromGroup($index); - } - else - { $index++; }; - }; - }; - - - # If there's no order, we still want to sort the new additions. - - if ($groupEntry->Flags() & ::MENU_GROUP_UNSORTED()) - { - if ($newEntriesIndex != -1) - { - my @newEntries = - @{$groupEntry->GroupContent()}[$newEntriesIndex..scalar @{$groupEntry->GroupContent()} - 1]; - - @newEntries = sort { $self->CompareEntries($a, $b) } @newEntries; - - foreach my $newEntry (@newEntries) - { - $groupEntry->GroupContent()->[$newEntriesIndex] = $newEntry; - $newEntriesIndex++; - }; - }; - } - - elsif ($groupEntry->Flags() & ::MENU_GROUP_EVERYTHINGSORTED()) - { - @{$groupEntry->GroupContent()} = sort { $self->CompareEntries($a, $b) } @{$groupEntry->GroupContent()}; - } - - elsif ( ($groupEntry->Flags() & ::MENU_GROUP_FILESSORTED()) || - ($groupEntry->Flags() & ::MENU_GROUP_FILESANDGROUPSSORTED()) ) - { - my $groupContent = $groupEntry->GroupContent(); - my @newEntries; - - if ($newEntriesIndex != -1) - { @newEntries = splice( @$groupContent, $newEntriesIndex ); }; - - - # First resort the existing entries. - - # A couple of support functions. They're defined here instead of spun off into their own functions because they're only - # used here and to make them general we would need to add support for the other sort options. - - sub IsIncludedInSort #(groupEntry, entry) - { - my ($self, $groupEntry, $entry) = @_; - - return ($entry->Type() == ::MENU_FILE() || - ( $entry->Type() == ::MENU_GROUP() && - ($groupEntry->Flags() & ::MENU_GROUP_FILESANDGROUPSSORTED()) ) ); - }; - - sub IsSorted #(groupEntry) - { - my ($self, $groupEntry) = @_; - my $lastApplicable; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - # If the entry is applicable to the sort order... - if ($self->IsIncludedInSort($groupEntry, $entry)) - { - if (defined $lastApplicable) - { - if ($self->CompareEntries($entry, $lastApplicable) < 0) - { return undef; }; - }; - - $lastApplicable = $entry; - }; - }; - - return 1; - }; - - - # There's a good chance it's still sorted. They should only become unsorted if an auto-title changes. - if (!$self->IsSorted($groupEntry)) - { - # Crap. Okay, method one is to sort each group of continuous sortable elements. There's a possibility that doing - # this will cause the whole to become sorted again. We try this first, even though it isn't guaranteed to succeed, - # because it will restore the sort without moving any unsortable entries. - - # Copy it because we'll need the original if this fails. - my @originalGroupContent = @$groupContent; - - my $index = 0; - my $startSortable = 0; - - while (1) - { - # If index is on an unsortable entry or the end of the array... - if ($index == scalar @$groupContent || !$self->IsIncludedInSort($groupEntry, $groupContent->[$index])) - { - # If we have at least two sortable entries... - if ($index - $startSortable >= 2) - { - # Sort them. - my @sortableEntries = @{$groupContent}[$startSortable .. $index - 1]; - @sortableEntries = sort { $self->CompareEntries($a, $b) } @sortableEntries; - foreach my $sortableEntry (@sortableEntries) - { - $groupContent->[$startSortable] = $sortableEntry; - $startSortable++; - }; - }; - - if ($index == scalar @$groupContent) - { last; }; - - $startSortable = $index + 1; - }; - - $index++; - }; - - if (!$self->IsSorted($groupEntry)) - { - # Crap crap. Okay, now we do a full sort but with potential damage to the original structure. Each unsortable - # element is locked to the next sortable element. We sort the sortable elements, bringing all the unsortable - # pieces with them. - - my @pieces = ( [ ] ); - my $currentPiece = $pieces[0]; - - foreach my $entry (@originalGroupContent) - { - push @$currentPiece, $entry; - - # If the entry is sortable... - if ($self->IsIncludedInSort($groupEntry, $entry)) - { - $currentPiece = [ ]; - push @pieces, $currentPiece; - }; - }; - - my $lastUnsortablePiece; - - # If the last entry was sortable, we'll have an empty piece at the end. Drop it. - if (scalar @{$pieces[-1]} == 0) - { pop @pieces; } - - # If the last entry wasn't sortable, the last piece won't end with a sortable element. Save it, but remove it - # from the list. - else - { $lastUnsortablePiece = pop @pieces; }; - - # Sort the list. - @pieces = sort { $self->CompareEntries( $a->[-1], $b->[-1] ) } @pieces; - - # Copy it back to the original. - if (defined $lastUnsortablePiece) - { push @pieces, $lastUnsortablePiece; }; - - my $index = 0; - - foreach my $piece (@pieces) - { - foreach my $entry (@{$piece}) - { - $groupEntry->GroupContent()->[$index] = $entry; - $index++; - }; - }; - }; - }; - - - # Okay, the orginal entries are sorted now. Sort the new entries and apply. - - if (scalar @newEntries) - { - @newEntries = sort { $self->CompareEntries($a, $b) } @newEntries; - my @originalEntries = @$groupContent; - @$groupContent = ( ); - - while (1) - { - while (scalar @originalEntries && !$self->IsIncludedInSort($groupEntry, $originalEntries[0])) - { push @$groupContent, (shift @originalEntries); }; - - if (!scalar @originalEntries || !scalar @newEntries) - { last; }; - - while (scalar @newEntries && $self->CompareEntries($newEntries[0], $originalEntries[0]) < 0) - { push @$groupContent, (shift @newEntries); }; - - push @$groupContent, (shift @originalEntries); - - if (!scalar @originalEntries || !scalar @newEntries) - { last; }; - }; - - if (scalar @originalEntries) - { push @$groupContent, @originalEntries; } - elsif (scalar @newEntries) - { push @$groupContent, @newEntries; }; - }; - }; - - - # Now re-add the exceptions. - - if (defined $leadingGeneralIndex) - { - unshift @{$groupEntry->GroupContent()}, $leadingGeneralIndex; - }; - - if (defined $trailingIndexGroup) - { - $groupEntry->PushToGroup($trailingIndexGroup); - }; - - }; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_GROUP()) - { push @groupStack, $entry; }; - }; - }; - }; - - -# -# Function: CompareEntries -# -# A comparison function for use in sorting. Compares the two entries by their titles with <StringCompare()>, but in the case -# of a tie, puts <MENU_FILE> entries above <MENU_GROUP> entries. -# -sub CompareEntries #(a, b) - { - my ($self, $a, $b) = @_; - - my $result = ::StringCompare($a->Title(), $b->Title()); - - if ($result == 0) - { - if ($a->Type() == ::MENU_FILE() && $b->Type() == ::MENU_GROUP()) - { $result = -1; } - elsif ($a->Type() == ::MENU_GROUP() && $b->Type() == ::MENU_FILE()) - { $result = 1; }; - }; - - return $result; - }; - - -# -# Function: SharedDirectoriesOf -# -# Returns an array of all the directories shared by the files in the group. If none, returns an empty array. -# -sub SharedDirectoriesOf #(group) - { - my ($self, $groupEntry) = @_; - my @sharedDirectories; - - foreach my $entry (@{$groupEntry->GroupContent()}) - { - if ($entry->Type() == ::MENU_FILE()) - { - my @entryDirectories = NaturalDocs::File->SplitDirectories( (NaturalDocs::File->SplitPath($entry->Target()))[1] ); - - if (!scalar @sharedDirectories) - { @sharedDirectories = @entryDirectories; } - else - { ::ShortenToMatchStrings(\@sharedDirectories, \@entryDirectories); }; - - if (!scalar @sharedDirectories) - { last; }; - }; - }; - - return @sharedDirectories; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Menu/Entry.pm b/docs/doctool/Modules/NaturalDocs/Menu/Entry.pm deleted file mode 100644 index af443f5d..00000000 --- a/docs/doctool/Modules/NaturalDocs/Menu/Entry.pm +++ /dev/null @@ -1,201 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Menu::Entry -# -############################################################################### -# -# A class representing an entry in the menu. -# -############################################################################### - -# 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::Menu::Entry; - - -############################################################################### -# Group: Implementation - -# -# Constants: Members -# -# The object is implemented as a blessed arrayref with the indexes below. -# -# TYPE - The <MenuEntryType> -# TITLE - The title of the entry. -# TARGET - The target of the entry. If the type is <MENU_FILE>, it will be the source <FileName>. If the type is -# <MENU_LINK>, it will be the URL. If the type is <MENU_GROUP>, it will be an arrayref of -# <NaturalDocs::Menu::Entry> objects representing the group's content. If the type is <MENU_INDEX>, it will be -# a <TopicType>. -# FLAGS - Any <Menu Entry Flags> that apply. -# -use constant TYPE => 0; -use constant TITLE => 1; -use constant TARGET => 2; -use constant FLAGS => 3; -# DEPENDENCY: New() depends on the order of these constants. - - -############################################################################### -# Group: Functions - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# type - The <MenuEntryType>. -# title - The title of the entry. -# target - The target of the entry, if applicable. If the type is <MENU_FILE>, use the source <FileName>. If the type is -# <MENU_LINK>, use the URL. If the type is <MENU_INDEX>, use the <TopicType>. Otherwise set it to undef. -# flags - Any <Menu Entry Flags> that apply. -# -sub New #(type, title, target, flags) - { - # DEPENDENCY: This gode depends on the order of the constants. - - my $package = shift; - - my $object = [ @_ ]; - bless $object, $package; - - if ($object->[TYPE] == ::MENU_GROUP()) - { $object->[TARGET] = [ ]; }; - if (!defined $object->[FLAGS]) - { $object->[FLAGS] = 0; }; - - return $object; - }; - - -# Function: Type -# Returns the <MenuEntryType>. -sub Type - { return $_[0]->[TYPE]; }; - -# Function: Title -# Returns the title of the entry. -sub Title - { return $_[0]->[TITLE]; }; - -# Function: SetTitle -# Replaces the entry's title. -sub SetTitle #(title) - { $_[0]->[TITLE] = $_[1]; }; - -# -# Function: Target -# -# Returns the target of the entry, if applicable. If the type is <MENU_FILE>, it returns the source <FileName>. If the type is -# <MENU_LINK>, it returns the URL. If the type is <MENU_INDEX>, it returns the <TopicType>. Otherwise it returns undef. -# -sub Target - { - my $self = shift; - - # Group entries are the only time when target won't be undef when it should be. - if ($self->Type() == ::MENU_GROUP()) - { return undef; } - else - { return $self->[TARGET]; }; - }; - -# Function: SetTarget -# Replaces the entry's target. -sub SetTarget #(target) - { $_[0]->[TARGET] = $_[1]; }; - -# Function: Flags -# Returns the <Menu Entry Flags>. -sub Flags - { return $_[0]->[FLAGS]; }; - -# Function: SetFlags -# Replaces the <Menu Entry Flags>. -sub SetFlags #(flags) - { $_[0]->[FLAGS] = $_[1]; }; - - - -############################################################################### -# Group: Group Functions -# -# All of these functions assume the type is <MENU_GROUP>. Do *not* call any of these without checking <Type()> first. - - -# -# Function: GroupContent -# -# Returns an arrayref of <NaturalDocs::Menu::Entry> objects representing the contents of the -# group, or undef otherwise. This arrayref will always exist for <MENU_GROUP>'s and can be changed. -# -sub GroupContent - { - return $_[0]->[TARGET]; - }; - - -# -# Function: GroupIsEmpty -# -# If the type is <MENU_GROUP>, returns whether the group is empty. -# -sub GroupIsEmpty - { - my $self = shift; - return (scalar @{$self->GroupContent()} > 0); - }; - - -# -# Function: PushToGroup -# -# Pushes the entry to the end of the group content. -# -sub PushToGroup #(entry) - { - my ($self, $entry) = @_; - push @{$self->GroupContent()}, $entry; - }; - - -# -# Function: DeleteFromGroup -# -# Deletes an entry from the group content by index. -# -sub DeleteFromGroup #(index) - { - my ($self, $index) = @_; - - my $groupContent = $self->GroupContent(); - - splice( @$groupContent, $index, 1 ); - }; - - -# -# Function: MarkEndOfOriginal -# -# If the group doesn't already have one, adds a <MENU_ENDOFORIGINAL> entry to the end and sets the -# <MENU_GROUP_HASENDOFORIGINAL> flag. -# -sub MarkEndOfOriginal - { - my $self = shift; - - if (($self->Flags() & ::MENU_GROUP_HASENDOFORIGINAL()) == 0) - { - $self->PushToGroup( NaturalDocs::Menu::Entry->New(::MENU_ENDOFORIGINAL(), undef, undef, undef) ); - $self->SetFlags( $self->Flags() | ::MENU_GROUP_HASENDOFORIGINAL() ); - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/NDMarkup.pm b/docs/doctool/Modules/NaturalDocs/NDMarkup.pm deleted file mode 100644 index bc4dde66..00000000 --- a/docs/doctool/Modules/NaturalDocs/NDMarkup.pm +++ /dev/null @@ -1,76 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::NDMarkup -# -############################################################################### -# -# A package of support functions for dealing with <NDMarkup>. -# -# Usage and Dependencies: -# -# The package doesn't depend on any Natural Docs packages and is ready to use right away. -# -############################################################################### - -# 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::NDMarkup; - -# -# Function: ConvertAmpChars -# -# Substitutes certain characters with their <NDMarkup> amp chars. -# -# Parameters: -# -# text - The block of text to convert. -# -# Returns: -# -# The converted text block. -# -sub ConvertAmpChars #(text) - { - my ($self, $text) = @_; - - $text =~ s/&/&/g; - $text =~ s/</</g; - $text =~ s/>/>/g; - $text =~ s/\"/"/g; - - return $text; - }; - - -# -# Function: RestoreAmpChars -# -# Replaces <NDMarkup> amp chars with their original symbols. -# -# Parameters: -# -# text - The text to restore. -# -# Returns: -# -# The restored text. -# -sub RestoreAmpChars #(text) - { - my ($self, $text) = @_; - - $text =~ s/"/\"/g; - $text =~ s/>/>/g; - $text =~ s/</</g; - $text =~ s/&/&/g; - - return $text; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Parser.pm b/docs/doctool/Modules/NaturalDocs/Parser.pm deleted file mode 100644 index 87671817..00000000 --- a/docs/doctool/Modules/NaturalDocs/Parser.pm +++ /dev/null @@ -1,1209 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Parser -# -############################################################################### -# -# A package that coordinates source file parsing between the <NaturalDocs::Languages::Base>-derived objects and its own -# sub-packages such as <NaturalDocs::Parser::Native>. Also handles sending symbols to <NaturalDocs::SymbolTable> and -# other generic topic processing. -# -# Usage and Dependencies: -# -# - Prior to use, <NaturalDocs::Settings>, <NaturalDocs::Languages>, <NaturalDocs::Project>, <NaturalDocs::SymbolTable>, -# and <NaturalDocs::ClassHierarchy> must be initialized. <NaturalDocs::SymbolTable> and <NaturalDocs::ClassHierarchy> -# do not have to be fully resolved. -# -# - Aside from that, the package is ready to use right away. It does not have its own initialization function. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - -use NaturalDocs::Parser::ParsedTopic; -use NaturalDocs::Parser::Native; - -use strict; -use integer; - -package NaturalDocs::Parser; - - - -############################################################################### -# Group: Variables - - -# -# var: sourceFile -# -# The source <FileName> currently being parsed. -# -my $sourceFile; - -# -# var: language -# -# The language object for the file, derived from <NaturalDocs::Languages::Base>. -# -my $language; - -# -# Array: parsedFile -# -# An array of <NaturalDocs::Parser::ParsedTopic> objects. -# -my @parsedFile; - - -# -# bool: parsingForInformation -# Whether <ParseForInformation()> was called. If false, then <ParseForBuild()> was called. -# -my $parsingForInformation; - - - -############################################################################### -# Group: Functions - -# -# Function: ParseForInformation -# -# Parses the input file for information. Will update the information about the file in <NaturalDocs::SymbolTable> and -# <NaturalDocs::Project>. -# -# Parameters: -# -# file - The <FileName> to parse. -# -sub ParseForInformation #(file) - { - my ($self, $file) = @_; - $sourceFile = $file; - - $parsingForInformation = 1; - - # Watch this parse so we detect any changes. - NaturalDocs::SymbolTable->WatchFileForChanges($sourceFile); - NaturalDocs::ClassHierarchy->WatchFileForChanges($sourceFile); - - my $defaultMenuTitle = $self->Parse(); - - foreach my $topic (@parsedFile) - { - # Add a symbol for the topic. - - my $type = $topic->Type(); - if ($type eq ::TOPIC_ENUMERATION()) - { $type = ::TOPIC_TYPE(); }; - - NaturalDocs::SymbolTable->AddSymbol($topic->Symbol(), $sourceFile, $type, - $topic->Prototype(), $topic->Summary()); - - - # You can't put the function call directly in a while with a regex. It has to sit in a variable to work. - my $body = $topic->Body(); - - - # If it's a list or enum topic, add a symbol for each description list entry. - - if ($topic->IsList() || $topic->Type() eq ::TOPIC_ENUMERATION()) - { - # We'll hijack the enum constants to apply to non-enum behavior too. - my $behavior; - - if ($topic->Type() eq ::TOPIC_ENUMERATION()) - { - $type = ::TOPIC_CONSTANT(); - $behavior = $language->EnumValues(); - } - elsif (NaturalDocs::Topics->TypeInfo($topic->Type())->Scope() == ::SCOPE_ALWAYS_GLOBAL()) - { - $behavior = ::ENUM_GLOBAL(); - } - else - { - $behavior = ::ENUM_UNDER_PARENT(); - }; - - while ($body =~ /<ds>([^<]+)<\/ds><dd>(.*?)<\/dd>/g) - { - my ($listTextSymbol, $listSummary) = ($1, $2); - - $listTextSymbol = NaturalDocs::NDMarkup->RestoreAmpChars($listTextSymbol); - my $listSymbol = NaturalDocs::SymbolString->FromText($listTextSymbol); - - if ($behavior == ::ENUM_UNDER_PARENT()) - { $listSymbol = NaturalDocs::SymbolString->Join($topic->Package(), $listSymbol); } - elsif ($behavior == ::ENUM_UNDER_TYPE()) - { $listSymbol = NaturalDocs::SymbolString->Join($topic->Symbol(), $listSymbol); }; - - NaturalDocs::SymbolTable->AddSymbol($listSymbol, $sourceFile, $type, undef, - $self->GetSummaryFromDescriptionList($listSummary)); - }; - }; - - - # Add references in the topic. - - while ($body =~ /<link>([^<]+)<\/link>/g) - { - my $linkText = NaturalDocs::NDMarkup->RestoreAmpChars($1); - my $linkSymbol = NaturalDocs::SymbolString->FromText($linkText); - - NaturalDocs::SymbolTable->AddReference(::REFERENCE_TEXT(), $linkSymbol, - $topic->Package(), $topic->Using(), $sourceFile); - }; - }; - - # Handle any changes to the file. - NaturalDocs::ClassHierarchy->AnalyzeChanges(); - NaturalDocs::SymbolTable->AnalyzeChanges(); - - # Update project on the file's characteristics. - my $hasContent = (scalar @parsedFile > 0); - - NaturalDocs::Project->SetHasContent($sourceFile, $hasContent); - if ($hasContent) - { NaturalDocs::Project->SetDefaultMenuTitle($sourceFile, $defaultMenuTitle); }; - - # We don't need to keep this around. - @parsedFile = ( ); - }; - - -# -# Function: ParseForBuild -# -# Parses the input file for building, returning it as a <NaturalDocs::Parser::ParsedTopic> arrayref. -# -# Note that all new and changed files should be parsed for symbols via <ParseForInformation()> before calling this function on -# *any* file. The reason is that <NaturalDocs::SymbolTable> needs to know about all the symbol definitions and references to -# resolve them properly. -# -# Parameters: -# -# file - The <FileName> to parse for building. -# -# Returns: -# -# An arrayref of the source file as <NaturalDocs::Parser::ParsedTopic> objects. -# -sub ParseForBuild #(file) - { - my ($self, $file) = @_; - $sourceFile = $file; - - $parsingForInformation = undef; - - $self->Parse(); - - return \@parsedFile; - }; - - - -############################################################################### -# Group: Interface Functions - - -# -# Function: OnComment -# -# The function called by <NaturalDocs::Languages::Base>-derived objects when their parsers encounter a comment -# suitable for documentation. -# -# Parameters: -# -# commentLines - An arrayref of the comment's lines. The language's comment symbols should be converted to spaces, -# and there should be no line break characters at the end of each line. *The original memory will be -# changed.* -# lineNumber - The line number of the first of the comment lines. -# -# Returns: -# -# The number of topics created by this comment, or zero if none. -# -sub OnComment #(commentLines, lineNumber) - { - my ($self, $commentLines, $lineNumber) = @_; - - $self->CleanComment($commentLines); - - return NaturalDocs::Parser::Native->ParseComment($commentLines, $lineNumber, \@parsedFile); - }; - - -# -# Function: OnClass -# -# A function called by <NaturalDocs::Languages::Base>-derived objects when their parsers encounter a class declaration. -# -# Parameters: -# -# class - The <SymbolString> of the class encountered. -# -sub OnClass #(class) - { - my ($self, $class) = @_; - - if ($parsingForInformation) - { NaturalDocs::ClassHierarchy->AddClass($sourceFile, $class); }; - }; - - -# -# Function: OnClassParent -# -# A function called by <NaturalDocs::Languages::Base>-derived objects when their parsers encounter a declaration of -# inheritance. -# -# Parameters: -# -# class - The <SymbolString> of the class we're in. -# parent - The <SymbolString> of the class it inherits. -# scope - The package <SymbolString> that the reference appeared in. -# using - An arrayref of package <SymbolStrings> that the reference has access to via "using" statements. -# resolvingFlags - Any <Resolving Flags> to be used when resolving the reference. <RESOLVE_NOPLURAL> is added -# automatically since that would never apply to source code. -# -sub OnClassParent #(class, parent, scope, using, resolvingFlags) - { - my ($self, $class, $parent, $scope, $using, $resolvingFlags) = @_; - - if ($parsingForInformation) - { - NaturalDocs::ClassHierarchy->AddParentReference($sourceFile, $class, $parent, $scope, $using, - $resolvingFlags | ::RESOLVE_NOPLURAL()); - }; - }; - - - -############################################################################### -# Group: Support Functions - - -# Function: Parse -# -# Opens the source file and parses process. Most of the actual parsing is done in <NaturalDocs::Languages::Base->ParseFile()> -# and <OnComment()>, though. -# -# *Do not call externally.* Rather, call <ParseForInformation()> or <ParseForBuild()>. -# -# Returns: -# -# The default menu title of the file. Will be the <FileName> if nothing better is found. -# -sub Parse - { - my ($self) = @_; - - NaturalDocs::Error->OnStartParsing($sourceFile); - - $language = NaturalDocs::Languages->LanguageOf($sourceFile); - NaturalDocs::Parser::Native->Start(); - @parsedFile = ( ); - - my ($autoTopics, $scopeRecord) = $language->ParseFile($sourceFile, \@parsedFile); - - - $self->AddToClassHierarchy(); - - $self->BreakLists(); - - if (defined $autoTopics) - { - if (defined $scopeRecord) - { $self->RepairPackages($autoTopics, $scopeRecord); }; - - $self->MergeAutoTopics($language, $autoTopics); - }; - - # We don't need to do this if there aren't any auto-topics because the only package changes would be implied by the comments. - if (defined $autoTopics) - { $self->AddPackageDelineators(); }; - - if (!NaturalDocs::Settings->NoAutoGroup()) - { $self->MakeAutoGroups($autoTopics); }; - - - # Set the menu title. - - my $defaultMenuTitle = $sourceFile; - - if (scalar @parsedFile) - { - # If there's only one topic, it's title overrides the file name. Certain topic types override the file name as well. - if (scalar @parsedFile == 1 || NaturalDocs::Topics->TypeInfo( $parsedFile[0]->Type() )->PageTitleIfFirst() ) - { - $defaultMenuTitle = $parsedFile[0]->Title(); - } - else - { - # If the title ended up being the file name, add a leading section for it. - my $name; - - my ($inputDirectory, $relativePath) = NaturalDocs::Settings->SplitFromInputDirectory($sourceFile); - - my ($volume, $dirString, $file) = NaturalDocs::File->SplitPath($relativePath); - my @directories = NaturalDocs::File->SplitDirectories($dirString); - - if (scalar @directories > 2) - { - $dirString = NaturalDocs::File->JoinDirectories('...', $directories[-2], $directories[-1]); - $name = NaturalDocs::File->JoinPath(undef, $dirString, $file); - } - else - { - $name = $relativePath; - } - - unshift @parsedFile, - NaturalDocs::Parser::ParsedTopic->New(::TOPIC_FILE(), $name, undef, undef, undef, undef, undef, 1, undef); - }; - }; - - NaturalDocs::Error->OnEndParsing($sourceFile); - - return $defaultMenuTitle; - }; - - -# -# Function: CleanComment -# -# Removes any extraneous formatting and whitespace from the comment. Eliminates comment boxes, horizontal lines, leading -# and trailing line breaks, trailing whitespace from lines, and expands all tab characters. It keeps leading whitespace, though, -# since it may be needed for example code, and multiple blank lines, since the original line numbers are needed. -# -# Parameters: -# -# commentLines - An arrayref of the comment lines to clean. *The original memory will be changed.* Lines should have the -# language's comment symbols replaced by spaces and not have a trailing line break. -# -sub CleanComment #(commentLines) - { - my ($self, $commentLines) = @_; - - use constant DONT_KNOW => 0; - use constant IS_UNIFORM => 1; - use constant IS_UNIFORM_IF_AT_END => 2; - use constant IS_NOT_UNIFORM => 3; - - my $leftSide = DONT_KNOW; - my $rightSide = DONT_KNOW; - my $leftSideChar; - my $rightSideChar; - - my $index = 0; - my $tabLength = NaturalDocs::Settings->TabLength(); - - while ($index < scalar @$commentLines) - { - # Strip trailing whitespace from the original. - - $commentLines->[$index] =~ s/[ \t]+$//; - - - # Expand tabs in the original. This method is almost six times faster than Text::Tabs' method. - - my $tabIndex = index($commentLines->[$index], "\t"); - - while ($tabIndex != -1) - { - substr( $commentLines->[$index], $tabIndex, 1, ' ' x ($tabLength - ($tabIndex % $tabLength)) ); - $tabIndex = index($commentLines->[$index], "\t", $tabIndex); - }; - - - # Make a working copy and strip leading whitespace as well. This has to be done after tabs are expanded because - # stripping indentation could change how far tabs are expanded. - - my $line = $commentLines->[$index]; - $line =~ s/^ +//; - - # If the line is blank... - if (!length $line) - { - # If we have a potential vertical line, this only acceptable if it's at the end of the comment. - if ($leftSide == IS_UNIFORM) - { $leftSide = IS_UNIFORM_IF_AT_END; }; - if ($rightSide == IS_UNIFORM) - { $rightSide = IS_UNIFORM_IF_AT_END; }; - } - - # If there's at least four symbols in a row, it's a horizontal line. The second regex supports differing edge characters. It - # doesn't matter if any of this matches the left and right side symbols. - elsif ($line =~ /^([^a-zA-Z0-9 ])\1{3,}$/ || - $line =~ /^([^a-zA-Z0-9 ])\1*([^a-zA-Z0-9 ])\2{3,}([^a-zA-Z0-9 ])\3*$/) - { - # Convert the original to a blank line. - $commentLines->[$index] = ''; - - # This has no effect on the vertical line detection. - } - - # If the line is not blank or a horizontal line... - else - { - # More content means any previous blank lines are no longer tolerated in vertical line detection. They are only - # acceptable at the end of the comment. - - if ($leftSide == IS_UNIFORM_IF_AT_END) - { $leftSide = IS_NOT_UNIFORM; }; - if ($rightSide == IS_UNIFORM_IF_AT_END) - { $rightSide = IS_NOT_UNIFORM; }; - - - # Detect vertical lines. Lines are only lines if they are followed by whitespace or a connected horizontal line. - # Otherwise we may accidentally detect lines from short comments that just happen to have every first or last - # character the same. - - if ($leftSide != IS_NOT_UNIFORM) - { - if ($line =~ /^([^a-zA-Z0-9])\1*(?: |$)/) - { - if ($leftSide == DONT_KNOW) - { - $leftSide = IS_UNIFORM; - $leftSideChar = $1; - } - else # ($leftSide == IS_UNIFORM) Other choices already ruled out. - { - if ($leftSideChar ne $1) - { $leftSide = IS_NOT_UNIFORM; }; - }; - } - # We'll tolerate the lack of symbols on the left on the first line, because it may be a - # /* Function: Whatever - # * Description. - # */ - # comment which would have the leading /* blanked out. - elsif ($index != 0) - { - $leftSide = IS_NOT_UNIFORM; - }; - }; - - if ($rightSide != IS_NOT_UNIFORM) - { - if ($line =~ / ([^a-zA-Z0-9])\1*$/) - { - if ($rightSide == DONT_KNOW) - { - $rightSide = IS_UNIFORM; - $rightSideChar = $1; - } - else # ($rightSide == IS_UNIFORM) Other choices already ruled out. - { - if ($rightSideChar ne $1) - { $rightSide = IS_NOT_UNIFORM; }; - }; - } - else - { - $rightSide = IS_NOT_UNIFORM; - }; - }; - - # We'll remove vertical lines later if they're uniform throughout the entire comment. - }; - - $index++; - }; - - - if ($leftSide == IS_UNIFORM_IF_AT_END) - { $leftSide = IS_UNIFORM; }; - if ($rightSide == IS_UNIFORM_IF_AT_END) - { $rightSide = IS_UNIFORM; }; - - - $index = 0; - - while ($index < scalar @$commentLines) - { - # Clear vertical lines. - - if ($leftSide == IS_UNIFORM) - { - # This works because every line should either start this way, be blank, or be the first line that doesn't start with a symbol. - $commentLines->[$index] =~ s/^ *([^a-zA-Z0-9 ])\1*//; - }; - - if ($rightSide == IS_UNIFORM) - { - $commentLines->[$index] =~ s/ *([^a-zA-Z0-9 ])\1*$//; - }; - - - # Clear horizontal lines again if there were vertical lines. This catches lines that were separated from the verticals by - # whitespace. We couldn't do this in the first loop because that would make the regexes over-tolerant. - - if ($leftSide == IS_UNIFORM || $rightSide == IS_UNIFORM) - { - $commentLines->[$index] =~ s/^ *([^a-zA-Z0-9 ])\1{3,}$//; - $commentLines->[$index] =~ s/^ *([^a-zA-Z0-9 ])\1*([^a-zA-Z0-9 ])\2{3,}([^a-zA-Z0-9 ])\3*$//; - }; - - - $index++; - }; - - }; - - - -############################################################################### -# Group: Processing Functions - - -# -# Function: RepairPackages -# -# Recalculates the packages for all comment topics using the auto-topics and the scope record. Call this *before* calling -# <MergeAutoTopics()>. -# -# Parameters: -# -# autoTopics - A reference to the list of automatically generated <NaturalDocs::Parser::ParsedTopics>. -# scopeRecord - A reference to an array of <NaturalDocs::Languages::Advanced::ScopeChanges>. -# -sub RepairPackages #(autoTopics, scopeRecord) - { - my ($self, $autoTopics, $scopeRecord) = @_; - - my $topicIndex = 0; - my $autoTopicIndex = 0; - my $scopeIndex = 0; - - my $topic = $parsedFile[0]; - my $autoTopic = $autoTopics->[0]; - my $scopeChange = $scopeRecord->[0]; - - my $currentPackage; - my $inFakePackage; - - while (defined $topic) - { - # First update the scope via the record if its defined and has the lowest line number. - if (defined $scopeChange && - $scopeChange->LineNumber() <= $topic->LineNumber() && - (!defined $autoTopic || $scopeChange->LineNumber() <= $autoTopic->LineNumber()) ) - { - $currentPackage = $scopeChange->Scope(); - $scopeIndex++; - $scopeChange = $scopeRecord->[$scopeIndex]; # Will be undef when past end. - $inFakePackage = undef; - } - - # Next try to end a fake scope with an auto topic if its defined and has the lowest line number. - elsif (defined $autoTopic && - $autoTopic->LineNumber() <= $topic->LineNumber()) - { - if ($inFakePackage) - { - $currentPackage = $autoTopic->Package(); - $inFakePackage = undef; - }; - - $autoTopicIndex++; - $autoTopic = $autoTopics->[$autoTopicIndex]; # Will be undef when past end. - } - - - # Finally try to handle the topic, since it has the lowest line number. - else - { - my $scope = NaturalDocs::Topics->TypeInfo($topic->Type())->Scope(); - - if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) - { - # They should already have the correct class and scope. - $currentPackage = $topic->Package(); - $inFakePackage = 1; - } - else - { - # Fix the package of everything else. - - # Note that the first function or variable topic to appear in a fake package will assume that package even if it turns out - # to be incorrect in the actual code, since the topic will come before the auto-topic. This will be corrected in - # MergeAutoTopics(). - - $topic->SetPackage($currentPackage); - }; - - $topicIndex++; - $topic = $parsedFile[$topicIndex]; # Will be undef when past end. - }; - }; - - }; - - -# -# Function: MergeAutoTopics -# -# Merges the automatically generated topics into the file. If an auto-topic matches an existing topic, it will have it's prototype -# and package transferred. If it doesn't, the auto-topic will be inserted into the list unless -# <NaturalDocs::Settings->DocumentedOnly()> is set. -# -# Parameters: -# -# language - The <NaturalDocs::Languages::Base>-derived class for the file. -# autoTopics - A reference to the list of automatically generated topics. -# -sub MergeAutoTopics #(language, autoTopics) - { - my ($self, $language, $autoTopics) = @_; - - my $topicIndex = 0; - my $autoTopicIndex = 0; - - # Keys are topic types, values are existence hashrefs of titles. - my %topicsInLists; - - while ($topicIndex < scalar @parsedFile && $autoTopicIndex < scalar @$autoTopics) - { - my $topic = $parsedFile[$topicIndex]; - my $autoTopic = $autoTopics->[$autoTopicIndex]; - - my $cleanTitle = $topic->Title(); - $cleanTitle =~ s/[\t ]*\([^\(]*$//; - - # Add the auto-topic if it's higher in the file than the current topic. - if ($autoTopic->LineNumber < $topic->LineNumber()) - { - if (exists $topicsInLists{$autoTopic->Type()} && - exists $topicsInLists{$autoTopic->Type()}->{$autoTopic->Title()}) - { - # Remove it from the list so a second one with the same name will be added. - delete $topicsInLists{$autoTopic->Type()}->{$autoTopic->Title()}; - } - elsif (!NaturalDocs::Settings->DocumentedOnly()) - { - splice(@parsedFile, $topicIndex, 0, $autoTopic); - $topicIndex++; - }; - - $autoTopicIndex++; - } - - # Transfer information if we have a match. - elsif ($topic->Type() == $autoTopic->Type() && index($autoTopic->Title(), $cleanTitle) != -1) - { - $topic->SetType($autoTopic->Type()); - $topic->SetPrototype($autoTopic->Prototype()); - - if (NaturalDocs::Topics->TypeInfo($topic->Type())->Scope() != ::SCOPE_START()) - { $topic->SetPackage($autoTopic->Package()); }; - - $topicIndex++; - $autoTopicIndex++; - } - - # Extract topics in lists. - elsif ($topic->IsList()) - { - if (!exists $topicsInLists{$topic->Type()}) - { $topicsInLists{$topic->Type()} = { }; }; - - my $body = $topic->Body(); - - while ($body =~ /<ds>([^<]+)<\/ds>/g) - { $topicsInLists{$topic->Type()}->{NaturalDocs::NDMarkup->RestoreAmpChars($1)} = 1; }; - - $topicIndex++; - } - - # Otherwise there's no match. Skip the topic. The auto-topic will be added later. - else - { - $topicIndex++; - } - }; - - # Add any auto-topics remaining. - if ($autoTopicIndex < scalar @$autoTopics && !NaturalDocs::Settings->DocumentedOnly()) - { - push @parsedFile, @$autoTopics[$autoTopicIndex..scalar @$autoTopics-1]; - }; - }; - - -# -# Function: MakeAutoGroups -# -# Creates group topics for files that do not have them. -# -sub MakeAutoGroups - { - my ($self) = @_; - - # No groups only one topic. - if (scalar @parsedFile < 2) - { return; }; - - my $index = 0; - my $startStretch = 0; - - # Skip the first entry if its the page title. - if (NaturalDocs::Topics->TypeInfo( $parsedFile[0]->Type() )->PageTitleIfFirst()) - { - $index = 1; - $startStretch = 1; - }; - - # Make auto-groups for each stretch between scope-altering topics. - while ($index < scalar @parsedFile) - { - my $scope = NaturalDocs::Topics->TypeInfo($parsedFile[$index]->Type())->Scope(); - - if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END()) - { - if ($index > $startStretch) - { $index += $self->MakeAutoGroupsFor($startStretch, $index); }; - - $startStretch = $index + 1; - }; - - $index++; - }; - - if ($index > $startStretch) - { $self->MakeAutoGroupsFor($startStretch, $index); }; - }; - - -# -# Function: MakeAutoGroupsFor -# -# Creates group topics for sections of files that do not have them. A support function for <MakeAutoGroups()>. -# -# Parameters: -# -# startIndex - The index to start at. -# endIndex - The index to end at. Not inclusive. -# -# Returns: -# -# The number of group topics added. -# -sub MakeAutoGroupsFor #(startIndex, endIndex) - { - my ($self, $startIndex, $endIndex) = @_; - - # No groups if any are defined already. - for (my $i = $startIndex; $i < $endIndex; $i++) - { - if ($parsedFile[$i]->Type() eq ::TOPIC_GROUP()) - { return 0; }; - }; - - - use constant COUNT => 0; - use constant TYPE => 1; - use constant SECOND_TYPE => 2; - use constant SIZE => 3; - - # This is an array of ( count, type, secondType ) triples. Count and Type will always be filled in; count is the number of - # consecutive topics of type. On the second pass, if small groups are combined secondType will be filled in. There will not be - # more than two types per group. - my @groups; - my $groupIndex = 0; - - - # First pass: Determine all the groups. - - my $i = $startIndex; - my $currentType; - - while ($i < $endIndex) - { - if (!defined $currentType || ($parsedFile[$i]->Type() ne $currentType && $parsedFile[$i]->Type() ne ::TOPIC_GENERIC()) ) - { - if (defined $currentType) - { $groupIndex += SIZE; }; - - $currentType = $parsedFile[$i]->Type(); - - $groups[$groupIndex + COUNT] = 1; - $groups[$groupIndex + TYPE] = $currentType; - } - else - { $groups[$groupIndex + COUNT]++; }; - - $i++; - }; - - - # Second pass: Combine groups based on "noise". Noise means types go from A to B to A at least once, and there are at least - # two groups in a row with three or less, and at least one of those groups is two or less. So 3, 3, 3 doesn't count as noise, but - # 3, 2, 3 does. - - $groupIndex = 0; - - # While there are at least three groups left... - while ($groupIndex < scalar @groups - (2 * SIZE)) - { - # If the group two places in front of this one has the same type... - if ($groups[$groupIndex + (2 * SIZE) + TYPE] eq $groups[$groupIndex + TYPE]) - { - # It means we went from A to B to A, which partially qualifies as noise. - - my $firstType = $groups[$groupIndex + TYPE]; - my $secondType = $groups[$groupIndex + SIZE + TYPE]; - - if (NaturalDocs::Topics->TypeInfo($firstType)->CanGroupWith($secondType) || - NaturalDocs::Topics->TypeInfo($secondType)->CanGroupWith($firstType)) - { - my $hasNoise; - - my $hasThrees; - my $hasTwosOrOnes; - - my $endIndex = $groupIndex; - - while ($endIndex < scalar @groups && - ($groups[$endIndex + TYPE] eq $firstType || $groups[$endIndex + TYPE] eq $secondType)) - { - if ($groups[$endIndex + COUNT] > 3) - { - # They must be consecutive to count. - $hasThrees = 0; - $hasTwosOrOnes = 0; - } - elsif ($groups[$endIndex + COUNT] == 3) - { - $hasThrees = 1; - - if ($hasTwosOrOnes) - { $hasNoise = 1; }; - } - else # < 3 - { - if ($hasThrees || $hasTwosOrOnes) - { $hasNoise = 1; }; - - $hasTwosOrOnes = 1; - }; - - $endIndex += SIZE; - }; - - if (!$hasNoise) - { - $groupIndex = $endIndex - SIZE; - } - else # hasNoise - { - $groups[$groupIndex + SECOND_TYPE] = $secondType; - - for (my $noiseIndex = $groupIndex + SIZE; $noiseIndex < $endIndex; $noiseIndex += SIZE) - { - $groups[$groupIndex + COUNT] += $groups[$noiseIndex + COUNT]; - }; - - splice(@groups, $groupIndex + SIZE, $endIndex - $groupIndex - SIZE); - - $groupIndex += SIZE; - }; - } - - else # They can't group together - { - $groupIndex += SIZE; - }; - } - - else - { $groupIndex += SIZE; }; - }; - - - # Finally, create group topics for the parsed file. - - $groupIndex = 0; - $i = $startIndex; - - while ($groupIndex < scalar @groups) - { - if ($groups[$groupIndex + TYPE] ne ::TOPIC_GENERIC()) - { - my $topic = $parsedFile[$i]; - my $title = NaturalDocs::Topics->NameOfType($groups[$groupIndex + TYPE], 1); - - if (defined $groups[$groupIndex + SECOND_TYPE]) - { $title .= ' and ' . NaturalDocs::Topics->NameOfType($groups[$groupIndex + SECOND_TYPE], 1); }; - - splice(@parsedFile, $i, 0, NaturalDocs::Parser::ParsedTopic->New(::TOPIC_GROUP(), - $title, - $topic->Package(), $topic->Using(), - undef, undef, undef, - $topic->LineNumber()) ); - $i++; - }; - - $i += $groups[$groupIndex + COUNT]; - $groupIndex += SIZE; - }; - - return (scalar @groups / SIZE); - }; - - -# -# Function: AddToClassHierarchy -# -# Adds any class topics to the class hierarchy, since they may not have been called with <OnClass()> if they didn't match up to -# an auto-topic. -# -sub AddToClassHierarchy - { - my ($self) = @_; - - foreach my $topic (@parsedFile) - { - if (NaturalDocs::Topics->TypeInfo( $topic->Type() )->ClassHierarchy()) - { - if ($topic->IsList()) - { - my $body = $topic->Body(); - - while ($body =~ /<ds>([^<]+)<\/ds>/g) - { - $self->OnClass( NaturalDocs::SymbolString->FromText( NaturalDocs::NDMarkup->RestoreAmpChars($1) ) ); - }; - } - else - { - $self->OnClass($topic->Package()); - }; - }; - }; - }; - - -# -# Function: AddPackageDelineators -# -# Adds section and class topics to make sure the package is correctly represented in the documentation. Should be called last in -# this process. -# -sub AddPackageDelineators - { - my ($self) = @_; - - my $index = 0; - my $currentPackage; - - # Values are the arrayref [ title, type ]; - my %usedPackages; - - while ($index < scalar @parsedFile) - { - my $topic = $parsedFile[$index]; - - if ($topic->Package() ne $currentPackage) - { - $currentPackage = $topic->Package(); - my $scopeType = NaturalDocs::Topics->TypeInfo($topic->Type())->Scope(); - - if ($scopeType == ::SCOPE_START()) - { - $usedPackages{$currentPackage} = [ $topic->Title(), $topic->Type() ]; - } - elsif ($scopeType == ::SCOPE_END()) - { - my $newTopic; - - if (!defined $currentPackage) - { - $newTopic = NaturalDocs::Parser::ParsedTopic->New(::TOPIC_SECTION(), 'Global', - undef, undef, - undef, undef, undef, - $topic->LineNumber(), undef); - } - else - { - my ($title, $body, $summary, $type); - my @packageIdentifiers = NaturalDocs::SymbolString->IdentifiersOf($currentPackage); - - if (exists $usedPackages{$currentPackage}) - { - $title = $usedPackages{$currentPackage}->[0]; - $type = $usedPackages{$currentPackage}->[1]; - $body = '<p>(continued)</p>'; - $summary = '(continued)'; - } - else - { - $title = join($language->PackageSeparator(), @packageIdentifiers); - $type = ::TOPIC_CLASS(); - - # Body and summary stay undef. - - $usedPackages{$currentPackage} = $title; - }; - - my @titleIdentifiers = NaturalDocs::SymbolString->IdentifiersOf( NaturalDocs::SymbolString->FromText($title) ); - for (my $i = 0; $i < scalar @titleIdentifiers; $i++) - { pop @packageIdentifiers; }; - - $newTopic = NaturalDocs::Parser::ParsedTopic->New($type, $title, - NaturalDocs::SymbolString->Join(@packageIdentifiers), undef, - undef, $summary, $body, - $topic->LineNumber(), undef); - } - - splice(@parsedFile, $index, 0, $newTopic); - $index++; - } - }; - - $index++; - }; - }; - - -# -# Function: BreakLists -# -# Breaks list topics into individual topics. -# -sub BreakLists - { - my $self = shift; - - my $index = 0; - - while ($index < scalar @parsedFile) - { - my $topic = $parsedFile[$index]; - - if ($topic->IsList() && NaturalDocs::Topics->TypeInfo( $topic->Type() )->BreakLists()) - { - my $body = $topic->Body(); - - my @newTopics; - my $newBody; - - my $bodyIndex = 0; - - for (;;) - { - my $startList = index($body, '<dl>', $bodyIndex); - - if ($startList == -1) - { last; }; - - $newBody .= substr($body, $bodyIndex, $startList - $bodyIndex); - - my $endList = index($body, '</dl>', $startList); - my $listBody = substr($body, $startList, $endList - $startList); - - while ($listBody =~ /<ds>([^<]+)<\/ds><dd>(.*?)<\/dd>/g) - { - my ($symbol, $description) = ($1, $2); - - push @newTopics, NaturalDocs::Parser::ParsedTopic->New( $topic->Type(), $symbol, $topic->Package(), - $topic->Using(), undef, - $self->GetSummaryFromDescriptionList($description), - '<p>' . $description . '</p>', $topic->LineNumber(), - undef ); - }; - - $bodyIndex = $endList + 5; - }; - - $newBody .= substr($body, $bodyIndex); - - # Remove trailing headings. - $newBody =~ s/(?:<h>[^<]+<\/h>)+$//; - - # Remove empty headings. - $newBody =~ s/(?:<h>[^<]+<\/h>)+(<h>[^<]+<\/h>)/$1/g; - - if ($newBody) - { - unshift @newTopics, NaturalDocs::Parser::ParsedTopic->New( ::TOPIC_GROUP(), $topic->Title(), $topic->Package(), - $topic->Using(), undef, - $self->GetSummaryFromBody($newBody), $newBody, - $topic->LineNumber(), undef ); - }; - - splice(@parsedFile, $index, 1, @newTopics); - - $index += scalar @newTopics; - } - - else # not a list - { $index++; }; - }; - }; - - -# -# Function: GetSummaryFromBody -# -# Returns the summary text from the topic body. -# -# Parameters: -# -# body - The complete topic body, in <NDMarkup>. -# -# Returns: -# -# The topic summary, or undef if none. -# -sub GetSummaryFromBody #(body) - { - my ($self, $body) = @_; - - my $summary; - - # Extract the first sentence from the leading paragraph, if any. We'll tolerate a single header beforehand, but nothing else. - - if ($body =~ /^(?:<h>[^<]*<\/h>)?<p>(.*?)(<\/p>|[\.\!\?](?:[\)\}\'\ ]|"|>))/x) - { - $summary = $1; - - if ($2 ne '</p>') - { $summary .= $2; }; - }; - - return $summary; - }; - - -# -# Function: GetSummaryFromDescriptionList -# -# Returns the summary text from a description list entry. -# -# Parameters: -# -# description - The description in <NDMarkup>. Should be the content between the <dd></dd> tags only. -# -# Returns: -# -# The description summary, or undef if none. -# -sub GetSummaryFromDescriptionList #(description) - { - my ($self, $description) = @_; - - my $summary; - - if ($description =~ /^(.*?)($|[\.\!\?](?:[\)\}\'\ ]|"|>))/) - { $summary = $1 . $2; }; - - return $summary; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Parser/Native.pm b/docs/doctool/Modules/NaturalDocs/Parser/Native.pm deleted file mode 100644 index b88c7bac..00000000 --- a/docs/doctool/Modules/NaturalDocs/Parser/Native.pm +++ /dev/null @@ -1,926 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Parser::Native -# -############################################################################### -# -# A package that converts comments from Natural Docs' native format into <NaturalDocs::Parser::ParsedTopic> 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 <SymbolString> 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 <NaturalDocs::Parser::ParsedTopics> 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 <NaturalDocs::Parser::ParsedTopic> object for the passed parameters. Scope is gotten from -# the package variable <scope> instead of from the parameters. The summary is generated from the body. -# -# Parameters: -# -# type - The <TopicType>. -# title - The title of the topic. -# package - The package <SymbolString> the topic appears in. -# body - The topic's body in <NDMarkup>. -# lineNumber - The topic's line number. -# isList - Whether the topic is a list. -# -# Returns: -# -# The <NaturalDocs::Parser::ParsedTopic> 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 <NDMarkup>. -# -# 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 <NDMarkup>. -# -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() => '</p>', - TAG_BULLETLIST() => '</li></ul>', - TAG_DESCRIPTIONLIST() => '</dd></dl>', - TAG_HEADING() => '</h>', - TAG_PREFIXCODE() => '</code>', - TAG_TAGCODE() => '</code>' ); - - 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) . '</code>'; - $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 .= '<code>'; - $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) . '</code>'; - $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) . '</p>'; - $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 .= '</li><li>'; - } - else #($topLevelTag != TAG_BULLETLIST) - { - $output .= $tagEnders{$topLevelTag} . '<ul><li>'; - $topLevelTag = TAG_BULLETLIST; - }; - - $textBlock = $bulletedText; - - $prevLineBlank = undef; - } - - # If the line looks like a description list entry... - elsif ($commentLines->[$index] =~ /^(.+?) +- +([^ ].*)$/ && $topLevelTag != TAG_PARAGRAPH) - { - my $entry = $1; - my $description = $2; - - if (defined $textBlock) - { $output .= $self->RichFormatTextBlock($textBlock); }; - - if ($topLevelTag == TAG_DESCRIPTIONLIST) - { - $output .= '</dd>'; - } - else #($topLevelTag != TAG_DESCRIPTIONLIST) - { - $output .= $tagEnders{$topLevelTag} . '<dl>'; - $topLevelTag = TAG_DESCRIPTIONLIST; - }; - - if (($isList && !$ignoreListSymbols) || $type eq ::TOPIC_ENUMERATION()) - { - $output .= '<ds>' . NaturalDocs::NDMarkup->ConvertAmpChars($entry) . '</ds><dd>'; - } - else - { - $output .= '<de>' . NaturalDocs::NDMarkup->ConvertAmpChars($entry) . '</de><dd>'; - }; - - $textBlock = $description; - - $prevLineBlank = undef; - } - - # If the line could be a header... - elsif ($prevLineBlank && $commentLines->[$index] =~ /^(.*)([^ ]):$/) - { - my $headerText = $1 . $2; - - if (defined $textBlock) - { - $output .= $self->RichFormatTextBlock($textBlock); - $textBlock = undef; - } - - $output .= $tagEnders{$topLevelTag}; - $topLevelTag = TAG_NONE; - - $output .= '<h>' . $self->RichFormatTextBlock($headerText) . '</h>'; - - if ($type eq ::TOPIC_FUNCTION() && $isList) - { - $ignoreListSymbols = exists $functionListIgnoredHeadings{lc($headerText)}; - }; - - $prevLineBlank = undef; - } - - # If the line looks like a code tag... - elsif ($commentLines->[$index] =~ /^\( *(?:(?:start|begin)? +)?(?:table|code|example|diagram) *\)$/i) - { - if (defined $textBlock) - { - $output .= $self->RichFormatTextBlock($textBlock); - $textBlock = undef; - }; - - $output .= $tagEnders{$topLevelTag} . '<code>'; - $topLevelTag = TAG_TAGCODE; - } - - # If the line isn't any of those, we consider it normal text. - else - { - # A blank line followed by normal text ends lists. We don't handle this when we detect if the line's blank because - # we don't want blank lines between list items to break the list. - if ($prevLineBlank && ($topLevelTag == TAG_BULLETLIST || $topLevelTag == TAG_DESCRIPTIONLIST)) - { - $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag} . '<p>'; - - $topLevelTag = TAG_PARAGRAPH; - $textBlock = undef; - } - - elsif ($topLevelTag == TAG_NONE) - { - $output .= '<p>'; - $topLevelTag = TAG_PARAGRAPH; - # textBlock will already be undef. - }; - - if (defined $textBlock) - { $textBlock .= ' '; }; - - $textBlock .= $commentLines->[$index]; - - $prevLineBlank = undef; - }; - }; - - $index++; - }; - - # Clean up anything left dangling. - if (defined $textBlock) - { - $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag}; - } - elsif (defined $codeBlock) - { - $codeBlock =~ s/\n+$//; - $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '</code>'; - }; - - 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 <NDMarkup> 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 .= '<email>' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '</email>'; } - elsif ($linkText =~ /^(?:http|https|ftp|news|file)\:/i) - { $output .= '<url>' . NaturalDocs::NDMarkup->ConvertAmpChars($linkText) . '</url>'; } - else - { $output .= '<link>' . NaturalDocs::NDMarkup->ConvertAmpChars($linkText) . '</link>'; }; - } - - 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 .= '<b>'; - } - elsif ($bold && $tagType == POSSIBLE_CLOSING_TAG) - { - $bold = undef; - $output .= '</b>'; - } - 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 .= '<u>'; - } - elsif ($underline && $tagType == POSSIBLE_CLOSING_TAG) - { - $underline = undef; - #underlineHasWhitespace will be reset by the next opening underline. - $output .= '</u>'; - } - elsif ($underline && !$underlineHasWhitespace) - { - # If there's no whitespace between underline tags, all underscores are replaced by spaces so - # _some_underlined_text_ becomes <u>some underlined text</u>. 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 <url> and <email> 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. - (?<! [a-z0-9>] ) - - # 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] ) - - } - - {<email>$1<\/email>}igx; - - $output =~ s{ - # The previous character can't be an alphanumeric. - (?<! [a-z0-9>] ) - - # 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\-\=\~\@\#\%\&\_\+\/\;\:\?] ) - - } - {<url>$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 <RichFormatTextBlock()>. -# -# 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 <link>s, <linx>es, <link>'s, and <links>'. - ( $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 <RichFormatTextBlock()>. -# -# 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 deleted file mode 100644 index e978b985..00000000 --- a/docs/doctool/Modules/NaturalDocs/Parser/ParsedTopic.pm +++ /dev/null @@ -1,210 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Parser::ParsedTopic -# -############################################################################### -# -# A class for parsed topics of source files. Also encompasses some of the <TopicType>-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 <TopicType>. -# TITLE - The title of the topic. -# PACKAGE - The package <SymbolString> the topic appears in, or undef if none. -# USING - An arrayref of additional package <SymbolStrings> 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 <NDMarkup>. 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 <TopicType>. -# title - The title of the topic. -# package - The package <SymbolString> the topic appears in, or undef if none. -# using - An arrayref of additional package <SymbolStrings> 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 <NDMarkup>. 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 <TopicType>. -sub Type - { return $_[0]->[TYPE]; }; - -# Function: SetType -# Replaces the <TopicType>. -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 <SymbolString> defined by the topic. It is fully resolved and does _not_ need to be joined with <Package()>. -# -# Type-Specific Behavior: -# -# - If the <TopicType> 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 <New()>. -# -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 <SymbolString> that the topic appears in. -# -# Type-Specific Behavior: -# -# - If the <TopicType> has scope, the package will be generated from both the title and the package passed to <New()>, not -# just the package. -# - If the <TopicType> is always global, the package will be the one passed to <New()>, even though it isn't part of it's -# <Symbol()>. -# - Everything else's package will be what was passed to <New()>, 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 <New()>. Later calls -# to <Package()> will still be generated according to its type-specific behavior. -sub SetPackage #(package) - { $_[0]->[PACKAGE] = $_[1]; }; - -# Function: Using -# Returns an arrayref of additional scope <SymbolStrings> available to the topic via "using" statements, or undef if none. -sub Using - { return $_[0]->[USING]; }; - -# Function: SetUsing -# Replaces the using arrayref of sope <SymbolStrings>. -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 <NDMarkup>. -sub Summary - { return $_[0]->[SUMMARY]; }; - -# Function: Body -# Returns the topic's body, formatted in <NDMarkup>. May be undef. -sub Body - { return $_[0]->[BODY]; }; - -# Function: SetBody -# Replaces the topic's body, formatted in <NDMarkup>. 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; diff --git a/docs/doctool/Modules/NaturalDocs/Project.pm b/docs/doctool/Modules/NaturalDocs/Project.pm deleted file mode 100644 index 2575fa4c..00000000 --- a/docs/doctool/Modules/NaturalDocs/Project.pm +++ /dev/null @@ -1,966 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Project -# -############################################################################### -# -# A package that manages information about the files in the source tree, as well as the list of files that have to be parsed -# and built. -# -# Usage and Dependencies: -# -# - All the <Data File Functions> are available immediately, except for the status functions. -# -# - <ReparseEverything()> and <RebuildEverything()> are available immediately, because they may need to be called -# after <LoadConfigFileInfo()> but before <LoadSourceFileInfo()>. -# -# - Prior to <LoadConfigFileInfo()>, <NaturalDocs::Settings> must be initialized. -# -# - After <LoadConfigFileInfo()>, the status <Data File Functions> are available as well. -# -# - Prior to <LoadSourceFileInfo()>, <NaturalDocs::Settings> and <NaturalDocs::Languages> must be initialized. -# -# - After <LoadSourceFileInfo()>, the rest of the <Source File Functions> are available. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - -use NaturalDocs::Project::File; - -use strict; -use integer; - -package NaturalDocs::Project; - - -############################################################################### -# Group: Variables - -# -# handle: FH_FILEINFO -# -# The file handle for the file information file, <FileInfo.nd>. -# - - -# -# handle: FH_CONFIGFILEINFO -# -# The file handle for the config file information file, <ConfigFileInfo.nd>. -# - - -# -# hash: supportedFiles -# -# A hash of all the supported files in the input directory. The keys are the <FileNames>, and the values are -# <NaturalDocs::Project::File> objects. -# -my %supportedFiles; - -# -# hash: filesToParse -# -# An existence hash of all the <FileNames> that need to be parsed. -# -my %filesToParse; - -# -# hash: filesToBuild -# -# An existence hash of all the <FileNames> that need to be built. -# -my %filesToBuild; - -# -# hash: filesToPurge -# -# An existence hash of the <FileNames> that had Natural Docs content last time, but now either don't exist or no longer have -# content. -# -my %filesToPurge; - -# -# hash: unbuiltFilesWithContent -# -# An existence hash of all the <FileNames> that have Natural Docs content but are not part of <filesToBuild>. -# -my %unbuiltFilesWithContent; - - -# var: menuFileStatus -# The <FileStatus> of the project's menu file. -my $menuFileStatus; - -# var: mainTopicsFileStatus -# The <FileStatus> of the project's main topics file. -my $mainTopicsFileStatus; - -# var: userTopicsFileStatus -# The <FileStatus> of the project's user topics file. -my $userTopicsFileStatus; - -# var: mainLanguagesFileStatus -# The <FileStatus> of the project's main languages file. -my $mainLanguagesFileStatus; - -# var: userLanguagesFileStatus -# The <FileStatus> of the project's user languages file. -my $userLanguagesFileStatus; - -# bool: reparseEverything -# Whether all the source files need to be reparsed. -my $reparseEverything; - -# bool: rebuildEverything -# Whether all the source files need to be rebuilt. -my $rebuildEverything; - -# hash: mostUsedLanguage -# The name of the most used language. Doesn't include text files. -my $mostUsedLanguage; - - - -############################################################################### -# Group: Files - - -# -# File: FileInfo.nd -# -# An index of the state of the files as of the last parse. Used to determine if files were added, deleted, or changed. -# -# Format: -# -# The format is a text file. -# -# > [VersionInt: app version] -# -# The beginning of the file is the <VersionInt> it was generated with. -# -# > [most used language name] -# -# Next is the name of the most used language in the source tree. Does not include text files. -# -# Each following line is -# -# > [file name] tab [last modification time] tab [has ND content (0 or 1)] tab [default menu title] \n -# -# Revisions: -# -# 1.3: -# -# - The line following the <VersionInt>, which was previously the last modification time of <Menu.txt>, was changed to -# the name of the most used language. -# -# 1.16: -# -# - File names are now absolute. Prior to 1.16, they were relative to the input directory since only one was allowed. -# -# 1.14: -# -# - The file was renamed from NaturalDocs.files to FileInfo.nd and moved into the Data subdirectory. -# -# 0.95: -# -# - The file version was changed to match the program version. Prior to 0.95, the version line was 1. Test for "1" instead -# of "1.0" to distinguish. -# - - -# -# File: ConfigFileInfo.nd -# -# An index of the state of the config files as of the last parse. -# -# Format: -# -# > [BINARY_FORMAT] -# > [VersionInt: app version] -# -# First is the standard <BINARY_FORMAT> <VersionInt> header. -# -# > [UInt32: last modification time of menu] -# > [UInt32: last modification of main topics file] -# > [UInt32: last modification of user topics file] -# > [UInt32: last modification of main languages file] -# > [UInt32: last modification of user languages file] -# -# Next are the last modification times of various configuration files as UInt32s in the standard Unix format. -# -# -# Revisions: -# -# 1.3: -# -# - The file was added to Natural Docs. Previously the last modification of <Menu.txt> was stored in <FileInfo.nd>, and -# <Topics.txt> and <Languages.txt> didn't exist. -# - - - -############################################################################### -# Group: File Functions - -# -# Function: LoadSourceFileInfo -# -# Loads the project file from disk and compares it against the files in the input directory. Project is loaded from -# <FileInfo.nd>. New and changed files will be added to <FilesToParse()>, and if they have content, -# <FilesToBuild()>. -# -# Will call <NaturalDocs::Languages->OnMostUsedLanguageChange()> if <MostUsedLanguage()> changes. -# -# Returns: -# -# Returns whether the project was changed in any way. -# -sub LoadSourceFileInfo - { - my ($self) = @_; - - $self->GetAllSupportedFiles(); - NaturalDocs::Languages->OnMostUsedLanguageKnown(); - - my $fileIsOkay; - my $version; - my $hasChanged; - - if (open(FH_FILEINFO, '<' . $self->FileInfoFile())) - { - # Check if the file is in the right format. - $version = NaturalDocs::Version->FromTextFile(\*FH_FILEINFO); - - # The project file need to be rebuilt for 1.16. The source files need to be reparsed and the output files rebuilt for 1.35. - # We'll tolerate the difference between 1.16 and 1.3 in the loader. - - if ($version >= NaturalDocs::Version->FromString('1.16') && $version <= NaturalDocs::Settings->AppVersion()) - { - $fileIsOkay = 1; - - if ($version < NaturalDocs::Version->FromString('1.35')) - { - $reparseEverything = 1; - $rebuildEverything = 1; - $hasChanged = 1; - }; - } - else - { - close(FH_FILEINFO); - $hasChanged = 1; - }; - }; - - - if ($fileIsOkay) - { - my %indexedFiles; - - - my $line = <FH_FILEINFO>; - ::XChomp(\$line); - - # Prior to 1.3 it was the last modification time of Menu.txt, which we ignore and treat as though the most used language - # changed. Prior to 1.32 the settings didn't transfer over correctly to Menu.txt so we need to behave that way again. - if ($version < NaturalDocs::Version->FromString('1.32') || lc($mostUsedLanguage) ne lc($line)) - { - $reparseEverything = 1; - NaturalDocs::SymbolTable->RebuildAllIndexes(); - }; - - - # Parse the rest of the file. - - while ($line = <FH_FILEINFO>) - { - ::XChomp(\$line); - my ($file, $modification, $hasContent, $menuTitle) = split(/\t/, $line, 4); - - # If the file no longer exists... - if (!exists $supportedFiles{$file}) - { - if ($hasContent) - { $filesToPurge{$file} = 1; }; - - $hasChanged = 1; - } - - # If the file still exists... - else - { - $indexedFiles{$file} = 1; - - # If the file changed... - if ($supportedFiles{$file}->LastModified() != $modification) - { - $supportedFiles{$file}->SetStatus(::FILE_CHANGED()); - $filesToParse{$file} = 1; - - # If the file loses its content, this will be removed by SetHasContent(). - if ($hasContent) - { $filesToBuild{$file} = 1; }; - - $hasChanged = 1; - } - - # If the file has not changed... - else - { - my $status; - - if ($rebuildEverything && $hasContent) - { - $status = ::FILE_CHANGED(); - - # If the file loses its content, this will be removed by SetHasContent(). - $filesToBuild{$file} = 1; - $hasChanged = 1; - } - else - { - $status = ::FILE_SAME(); - - if ($hasContent) - { $unbuiltFilesWithContent{$file} = 1; }; - }; - - if ($reparseEverything) - { - $status = ::FILE_CHANGED(); - - $filesToParse{$file} = 1; - $hasChanged = 1; - }; - - $supportedFiles{$file}->SetStatus($status); - }; - - $supportedFiles{$file}->SetHasContent($hasContent); - $supportedFiles{$file}->SetDefaultMenuTitle($menuTitle); - }; - }; - - close(FH_FILEINFO); - - - # Check for added files. - - if (scalar keys %supportedFiles > scalar keys %indexedFiles) - { - foreach my $file (keys %supportedFiles) - { - if (!exists $indexedFiles{$file}) - { - $supportedFiles{$file}->SetStatus(::FILE_NEW()); - $supportedFiles{$file}->SetDefaultMenuTitle($file); - $supportedFiles{$file}->SetHasContent(undef); - $filesToParse{$file} = 1; - # It will be added to filesToBuild if HasContent gets set to true when it's parsed. - $hasChanged = 1; - }; - }; - }; - } - - # If something's wrong with FileInfo.nd, everything is new. - else - { - foreach my $file (keys %supportedFiles) - { - $supportedFiles{$file}->SetStatus(::FILE_NEW()); - $supportedFiles{$file}->SetDefaultMenuTitle($file); - $supportedFiles{$file}->SetHasContent(undef); - $filesToParse{$file} = 1; - # It will be added to filesToBuild if HasContent gets set to true when it's parsed. - }; - - $hasChanged = 1; - }; - - - # There are other side effects, so we need to call this. - if ($rebuildEverything) - { $self->RebuildEverything(); }; - - - return $hasChanged; - }; - - -# -# Function: SaveSourceFileInfo -# -# Saves the source file info to disk. Everything is saved in <FileInfo.nd>. -# -sub SaveSourceFileInfo - { - my ($self) = @_; - - open(FH_FILEINFO, '>' . $self->FileInfoFile()) - or die "Couldn't save project file " . $self->FileInfoFile() . "\n"; - - NaturalDocs::Version->ToTextFile(\*FH_FILEINFO, NaturalDocs::Settings->AppVersion()); - - print FH_FILEINFO $mostUsedLanguage . "\n"; - - while (my ($fileName, $file) = each %supportedFiles) - { - print FH_FILEINFO $fileName . "\t" - . $file->LastModified() . "\t" - . ($file->HasContent() || '0') . "\t" - . $file->DefaultMenuTitle() . "\n"; - }; - - close(FH_FILEINFO); - }; - - -# -# Function: LoadConfigFileInfo -# -# Loads the config file info to disk. -# -sub LoadConfigFileInfo - { - my ($self) = @_; - - my $fileIsOkay; - my $version; - my $fileName = NaturalDocs::Project->ConfigFileInfoFile(); - - if (open(FH_CONFIGFILEINFO, '<' . $fileName)) - { - # See if it's binary. - binmode(FH_CONFIGFILEINFO); - - my $firstChar; - read(FH_CONFIGFILEINFO, $firstChar, 1); - - if ($firstChar == ::BINARY_FORMAT()) - { - $version = NaturalDocs::Version->FromBinaryFile(\*FH_CONFIGFILEINFO); - - # It hasn't changed since being introduced. - - if ($version <= NaturalDocs::Settings->AppVersion()) - { $fileIsOkay = 1; } - else - { close(FH_CONFIGFILEINFO); }; - } - - else # it's not in binary - { close(FH_CONFIGFILEINFO); }; - }; - - my @configFiles = ( $self->MenuFile(), \$menuFileStatus, - $self->MainTopicsFile(), \$mainTopicsFileStatus, - $self->UserTopicsFile(), \$userTopicsFileStatus, - $self->MainLanguagesFile(), \$mainLanguagesFileStatus, - $self->UserLanguagesFile(), \$userLanguagesFileStatus ); - - if ($fileIsOkay) - { - my $raw; - - read(FH_CONFIGFILEINFO, $raw, 20); - my @configFileDates = unpack('NNNNN', $raw); - - while (scalar @configFiles) - { - my $file = shift @configFiles; - my $fileStatus = shift @configFiles; - my $fileDate = shift @configFileDates; - - if (-e $file) - { - if ($fileDate == (stat($file))[9]) - { $$fileStatus = ::FILE_SAME(); } - else - { $$fileStatus = ::FILE_CHANGED(); }; - } - else - { $$fileStatus = ::FILE_DOESNTEXIST(); }; - }; - - close(FH_CONFIGFILEINFO); - } - else - { - while (scalar @configFiles) - { - my $file = shift @configFiles; - my $fileStatus = shift @configFiles; - - if (-e $file) - { $$fileStatus = ::FILE_CHANGED(); } - else - { $$fileStatus = ::FILE_DOESNTEXIST(); }; - }; - }; - - if ($menuFileStatus == ::FILE_SAME() && $rebuildEverything) - { $menuFileStatus = ::FILE_CHANGED(); }; - }; - - -# -# Function: SaveConfigFileInfo -# -# Saves the config file info to disk. You *must* save all other config files first, such as <Menu.txt> and <Topics.txt>. -# -sub SaveConfigFileInfo - { - my ($self) = @_; - - open (FH_CONFIGFILEINFO, '>' . NaturalDocs::Project->ConfigFileInfoFile()) - or die "Couldn't save " . NaturalDocs::Project->ConfigFileInfoFile() . ".\n"; - - binmode(FH_CONFIGFILEINFO); - - print FH_CONFIGFILEINFO '' . ::BINARY_FORMAT(); - - NaturalDocs::Version->ToBinaryFile(\*FH_CONFIGFILEINFO, NaturalDocs::Settings->AppVersion()); - - print FH_CONFIGFILEINFO pack('NNNNN', (stat($self->MenuFile()))[9], - (stat($self->MainTopicsFile()))[9], - (stat($self->UserTopicsFile()))[9], - (stat($self->MainLanguagesFile()))[9], - (stat($self->UserLanguagesFile()))[9] ); - - close(FH_CONFIGFILEINFO); - }; - - -# -# Function: MigrateOldFiles -# -# If the project uses the old file names used prior to 1.14, it converts them to the new file names. -# -sub MigrateOldFiles - { - my ($self) = @_; - - my $projectDirectory = NaturalDocs::Settings->ProjectDirectory(); - - # We use the menu file as a test to see if we're using the new format. - if (-e NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs_Menu.txt')) - { - # The Data subdirectory would have been created by NaturalDocs::Settings. - - rename( NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs_Menu.txt'), $self->MenuFile() ); - - if (-e NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.sym')) - { rename( NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.sym'), $self->SymbolTableFile() ); }; - - if (-e NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.files')) - { rename( NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.files'), $self->FileInfoFile() ); }; - - if (-e NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.m')) - { rename( NaturalDocs::File->JoinPaths($projectDirectory, 'NaturalDocs.m'), $self->PreviousMenuStateFile() ); }; - }; - }; - - - -############################################################################### -# Group: Data File Functions - - -# Function: FileInfoFile -# Returns the full path to the file information file. -sub FileInfoFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDataDirectory(), 'FileInfo.nd' ); }; - -# Function: ConfigFileInfoFile -# Returns the full path to the config file information file. -sub ConfigFileInfoFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDataDirectory(), 'ConfigFileInfo.nd' ); }; - -# Function: SymbolTableFile -# Returns the full path to the symbol table's data file. -sub SymbolTableFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDataDirectory(), 'SymbolTable.nd' ); }; - -# Function: ClassHierarchyFile -# Returns the full path to the class hierarchy's data file. -sub ClassHierarchyFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDataDirectory(), 'ClassHierarchy.nd' ); }; - -# Function: MenuFile -# Returns the full path to the project's menu file. -sub MenuFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), 'Menu.txt' ); }; - -# Function: MenuFileStatus -# Returns the <FileStatus> of the project's menu file. -sub MenuFileStatus - { return $menuFileStatus; }; - -# Function: MainTopicsFile -# Returns the full path to the main topics file. -sub MainTopicsFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ConfigDirectory(), 'Topics.txt' ); }; - -# Function: MainTopicsFileStatus -# Returns the <FileStatus> of the project's main topics file. -sub MainTopicsFileStatus - { return $mainTopicsFileStatus; }; - -# Function: UserTopicsFile -# Returns the full path to the user's topics file. -sub UserTopicsFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), 'Topics.txt' ); }; - -# Function: UserTopicsFileStatus -# Returns the <FileStatus> of the project's user topics file. -sub UserTopicsFileStatus - { return $userTopicsFileStatus; }; - -# Function: MainLanguagesFile -# Returns the full path to the main languages file. -sub MainLanguagesFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ConfigDirectory(), 'Languages.txt' ); }; - -# Function: MainLanguagesFileStatus -# Returns the <FileStatus> of the project's main languages file. -sub MainLanguagesFileStatus - { return $mainLanguagesFileStatus; }; - -# Function: UserLanguagesFile -# Returns the full path to the user's languages file. -sub UserLanguagesFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), 'Languages.txt' ); }; - -# Function: UserLanguagesFileStatus -# Returns the <FileStatus> of the project's user languages file. -sub UserLanguagesFileStatus - { return $userLanguagesFileStatus; }; - -# Function: SettingsFile -# Returns the full path to the project's settings file. -sub SettingsFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), 'Settings.txt' ); }; - -# Function: PreviousSettingsFile -# Returns the full path to the project's previous settings file. -sub PreviousSettingsFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDataDirectory(), 'PreviousSettings.nd' ); }; - -# Function: PreviousMenuStateFile -# Returns the full path to the project's previous menu state file. -sub PreviousMenuStateFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDataDirectory(), 'PreviousMenuState.nd' ); }; - -# Function: MenuBackupFile -# Returns the full path to the project's menu backup file, which is used to save the original menu in some situations. -sub MenuBackupFile - { return NaturalDocs::File->JoinPaths( NaturalDocs::Settings->ProjectDirectory(), 'Menu_Backup.txt' ); }; - - - -############################################################################### -# Group: Source File Functions - - -# Function: FilesToParse -# Returns an existence hashref of the <FileNames> to parse. This is not a copy of the data, so don't change it. -sub FilesToParse - { return \%filesToParse; }; - -# Function: FilesToBuild -# Returns an existence hashref of the <FileNames> to build. This is not a copy of the data, so don't change it. -sub FilesToBuild - { return \%filesToBuild; }; - -# Function: FilesToPurge -# Returns an existence hashref of the <FileNames> that had content last time, but now either don't anymore or were deleted. -# This is not a copy of the data, so don't change it. -sub FilesToPurge - { return \%filesToPurge; }; - -# -# Function: RebuildFile -# -# Adds the file to the list of files to build. This function will automatically filter out files that don't have Natural Docs content and -# files that are part of <FilesToPurge()>. If this gets called on a file and that file later gets Natural Docs content, it will be added. -# -# Parameters: -# -# file - The <FileName> to build or rebuild. -# -sub RebuildFile #(file) - { - my ($self, $file) = @_; - - # We don't want to add it to the build list if it doesn't exist, doesn't have Natural Docs content, or it's going to be purged. - # If it wasn't parsed yet and will later be found to have ND content, it will be added by SetHasContent(). - if (exists $supportedFiles{$file} && !exists $filesToPurge{$file} && $supportedFiles{$file}->HasContent()) - { - $filesToBuild{$file} = 1; - - if (exists $unbuiltFilesWithContent{$file}) - { delete $unbuiltFilesWithContent{$file}; }; - }; - }; - - -# -# Function: ReparseEverything -# -# Adds all supported files to the list of files to parse. This does not necessarily mean these files are going to be rebuilt. -# -sub ReparseEverything - { - my ($self) = @_; - - if (!$reparseEverything) - { - foreach my $file (keys %supportedFiles) - { - $filesToParse{$file} = 1; - }; - - $reparseEverything = 1; - }; - }; - - -# -# Function: RebuildEverything -# -# Adds all supported files to the list of files to build. This does not necessarily mean these files are going to be reparsed. -# -sub RebuildEverything - { - my ($self) = @_; - - foreach my $file (keys %unbuiltFilesWithContent) - { - $filesToBuild{$file} = 1; - }; - - %unbuiltFilesWithContent = ( ); - $rebuildEverything = 1; - - NaturalDocs::SymbolTable->RebuildAllIndexes(); - - if ($menuFileStatus == ::FILE_SAME()) - { $menuFileStatus = ::FILE_CHANGED(); }; - }; - - -# Function: UnbuiltFilesWithContent -# Returns an existence hashref of the <FileNames> that have Natural Docs content but are not part of <FilesToBuild()>. This is -# not a copy of the data so don't change it. -sub UnbuiltFilesWithContent - { return \%unbuiltFilesWithContent; }; - -# Function: FilesWithContent -# Returns and existence hashref of the <FileNames> that have Natural Docs content. -sub FilesWithContent - { - # Don't keep this one internally, but there's an easy way to make it. - return { %filesToBuild, %unbuiltFilesWithContent }; - }; - - -# -# Function: HasContent -# -# Returns whether the <FileName> contains Natural Docs content. -# -sub HasContent #(file) - { - my ($self, $file) = @_; - - if (exists $supportedFiles{$file}) - { return $supportedFiles{$file}->HasContent(); } - else - { return undef; }; - }; - - -# -# Function: SetHasContent -# -# Sets whether the <FileName> has Natural Docs content or not. -# -sub SetHasContent #(file, hasContent) - { - my ($self, $file, $hasContent) = @_; - - if (exists $supportedFiles{$file} && $supportedFiles{$file}->HasContent() != $hasContent) - { - # If the file now has content... - if ($hasContent) - { - $filesToBuild{$file} = 1; - } - - # If the file's content has been removed... - else - { - delete $filesToBuild{$file}; # may not be there - $filesToPurge{$file} = 1; - }; - - $supportedFiles{$file}->SetHasContent($hasContent); - }; - }; - - -# -# Function: StatusOf -# -# Returns the <FileStatus> of the passed <FileName>. -# -sub StatusOf #(file) - { - my ($self, $file) = @_; - - if (exists $supportedFiles{$file}) - { return $supportedFiles{$file}->Status(); } - else - { return ::FILE_DOESNTEXIST(); }; - }; - - -# -# Function: DefaultMenuTitleOf -# -# Returns the default menu title of the <FileName>. If one isn't specified, it returns the <FileName>. -# -sub DefaultMenuTitleOf #(file) - { - my ($self, $file) = @_; - - if (exists $supportedFiles{$file}) - { return $supportedFiles{$file}->DefaultMenuTitle(); } - else - { return $file; }; - }; - - -# -# Function: SetDefaultMenuTitle -# -# Sets the <FileName's> default menu title. -# -sub SetDefaultMenuTitle #(file, menuTitle) - { - my ($self, $file, $menuTitle) = @_; - - if (exists $supportedFiles{$file} && $supportedFiles{$file}->DefaultMenuTitle() ne $menuTitle) - { - $supportedFiles{$file}->SetDefaultMenuTitle($menuTitle); - NaturalDocs::Menu->OnDefaultTitleChange($file); - }; - }; - - -# -# Function: MostUsedLanguage -# -# Returns the name of the most used language in the source trees. Does not include text files. -# -sub MostUsedLanguage - { return $mostUsedLanguage; }; - - - -############################################################################### -# Group: Support Functions - -# -# Function: GetAllSupportedFiles -# -# Gets all the supported files in the passed directory and its subdirectories and puts them into <supportedFiles>. The only -# attribute that will be set is <NaturalDocs::Project::File->LastModified()>. Also sets <mostUsedLanguage>. -# -sub GetAllSupportedFiles - { - my ($self) = @_; - - my @directories = @{NaturalDocs::Settings->InputDirectories()}; - - # Keys are language names, values are counts. - my %languageCounts; - - - # Make an existence hash of excluded directories. - - my %excludedDirectories; - my $excludedDirectoryArrayRef = NaturalDocs::Settings->ExcludedInputDirectories(); - - foreach my $excludedDirectory (@$excludedDirectoryArrayRef) - { - if (NaturalDocs::File->IsCaseSensitive()) - { $excludedDirectories{$excludedDirectory} = 1; } - else - { $excludedDirectories{lc($excludedDirectory)} = 1; }; - }; - - - while (scalar @directories) - { - my $directory = pop @directories; - - opendir DIRECTORYHANDLE, $directory; - my @entries = readdir DIRECTORYHANDLE; - closedir DIRECTORYHANDLE; - - @entries = NaturalDocs::File->NoUpwards(@entries); - - foreach my $entry (@entries) - { - my $fullEntry = NaturalDocs::File->JoinPaths($directory, $entry); - - # If an entry is a directory, recurse. - if (-d $fullEntry) - { - # Join again with the noFile flag set in case the platform handles them differently. - $fullEntry = NaturalDocs::File->JoinPaths($directory, $entry, 1); - - if (NaturalDocs::File->IsCaseSensitive()) - { - if (!exists $excludedDirectories{$fullEntry}) - { push @directories, $fullEntry; }; - } - else - { - if (!exists $excludedDirectories{lc($fullEntry)}) - { push @directories, $fullEntry; }; - }; - } - - # Otherwise add it if it's a supported extension. - else - { - if (my $language = NaturalDocs::Languages->LanguageOf($fullEntry)) - { - $supportedFiles{$fullEntry} = NaturalDocs::Project::File->New(undef, (stat($fullEntry))[9], undef, undef); - $languageCounts{$language->Name()}++; - }; - }; - }; - }; - - - my $topCount = 0; - - while (my ($language, $count) = each %languageCounts) - { - if ($count > $topCount && $language ne 'Text File') - { - $topCount = $count; - $mostUsedLanguage = $language; - }; - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Project/File.pm b/docs/doctool/Modules/NaturalDocs/Project/File.pm deleted file mode 100644 index e901d837..00000000 --- a/docs/doctool/Modules/NaturalDocs/Project/File.pm +++ /dev/null @@ -1,113 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Project::File -# -############################################################################### -# -# A simple information class about project files. -# -############################################################################### - -# 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::Project::File; - - - -############################################################################### -# Group: Implementation - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The following constants are used as indexes. -# -# HAS_CONTENT - Whether the file contains Natural Docs content or not. -# LAST_MODIFIED - The integer timestamp of when the file was last modified. -# STATUS - <FileStatus> since the last build. -# DEFAULT_MENU_TITLE - The file's default title in the menu. -# - -# DEPENDENCY: New() depends on its parameter list being in the same order as these constants. If the order changes, New() -# needs to be changed. -use NaturalDocs::DefineMembers 'HAS_CONTENT', 'LAST_MODIFIED', 'STATUS', 'DEFAULT_MENU_TITLE'; - - -############################################################################### -# Group: Functions - -# -# Function: New -# -# Creates and returns a new file object. -# -# Parameters: -# -# hasContent - Whether the file contains Natural Docs content or not. -# lastModified - The integer timestamp of when the file was last modified. -# status - The <FileStatus> since the last build. -# defaultMenuTitle - The file's title in the menu. -# -# Returns: -# -# A reference to the new object. -# -sub New #(hasContent, lastModified, status, defaultMenuTitle) - { - # DEPENDENCY: This function depends on its parameter list being in the same order as the member constants. If either order - # changes, this function needs to be changed. - - my $package = shift; - - my $object = [ @_ ]; - bless $object, $package; - - return $object; - }; - -# Function: HasContent -# Returns whether the file contains Natural Docs content or not. -sub HasContent - { return $_[0]->[HAS_CONTENT]; }; - -# Function: SetHasContent -# Sets whether the file contains Natural Docs content or not. -sub SetHasContent #(hasContent) - { $_[0]->[HAS_CONTENT] = $_[1]; }; - -# Function: LastModified -# Returns the integer timestamp of when the file was last modified. -sub LastModified - { return $_[0]->[LAST_MODIFIED]; }; - -# Function: SetLastModified -# Sets the file's last modification timestamp. -sub SetLastModified #(lastModified) - { $_[0]->[LAST_MODIFIED] = $_[1]; }; - -# Function: Status -# Returns the <FileStatus> since the last build. -sub Status - { return $_[0]->[STATUS]; }; - -# Function: SetStatus -# Sets the <FileStatus> since the last build. -sub SetStatus #(status) - { $_[0]->[STATUS] = $_[1]; }; - -# Function: DefaultMenuTitle -# Returns the file's default title on the menu. -sub DefaultMenuTitle - { return $_[0]->[DEFAULT_MENU_TITLE]; }; - -# Function: SetDefaultMenuTitle -# Sets the file's default title on the menu. -sub SetDefaultMenuTitle #(menuTitle) - { $_[0]->[DEFAULT_MENU_TITLE] = $_[1]; }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/ReferenceString.pm b/docs/doctool/Modules/NaturalDocs/ReferenceString.pm deleted file mode 100644 index c9bca75a..00000000 --- a/docs/doctool/Modules/NaturalDocs/ReferenceString.pm +++ /dev/null @@ -1,301 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::ReferenceString -# -############################################################################### -# -# A package to manage <ReferenceString> handling throughout the program. -# -############################################################################### - -# 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::ReferenceString; - -use vars '@ISA', '@EXPORT'; -@ISA = 'Exporter'; -@EXPORT = ( 'BINARYREF_NOTYPE', 'BINARYREF_NORESOLVINGFLAGS' ); - - -# -# Constants: Binary Format Flags -# -# These flags can be combined to specify the format when using <ToBinaryFile()> and <FromBinaryFile()>. All are exported -# by default. -# -# BINARYREF_NOTYPE - Do not include the <ReferenceType>. -# BINARYREF_NORESOLVEFLAGS - Do not include the <Resolving Flags>. -# -use constant BINARYREF_NOTYPE => 0x01; -use constant BINARYREF_NORESOLVINGFLAGS => 0x02; - - -# -# -# Function: MakeFrom -# -# Encodes the passed information as a <ReferenceString>. The format of the string should be treated as opaque. However, the -# characteristic you can rely on is that the same string will always be made from the same parameters, and thus it's suitable -# for comparison and use as hash keys. -# -# Parameters: -# -# type - The <ReferenceType>. -# symbol - The <SymbolString> of the reference. -# scope - The scope <SymbolString> the reference appears in, or undef if none. -# using - An arrayref of scope <SymbolStrings> that are also available for checking due to the equivalent a "using" statement, -# or undef if none. -# resolvingFlags - The <Resolving Flags> to use with this reference. They are ignored if the type is <REFERENCE_TEXT>. -# -# Returns: -# -# The encoded <ReferenceString>. -# -sub MakeFrom #(type, symbol, scope, using, resolvingFlags) - { - my ($self, $type, $symbol, $scope, $using, $resolvingFlags) = @_; - - if ($type == ::REFERENCE_TEXT() || $resolvingFlags == 0) - { $resolvingFlags = undef; }; - - # The format is [type] 0x1E [resolving flags] \x1E [symbol] 0x1E [scope] ( 0x1E [using] )* - # The format of the symbol, scope, and usings are the identifiers separated by 0x1F characters. - # If scope is undef but using isn't, there will be two 0x1E's to signify the missing scope. - - my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol); - - my $string = $type . "\x1E" . $resolvingFlags . "\x1E" . join("\x1F", @identifiers); - - if (defined $scope) - { - @identifiers = NaturalDocs::SymbolString->IdentifiersOf($scope); - $string .= "\x1E" . join("\x1F", @identifiers); - }; - - if (defined $using) - { - my @usingStrings; - - foreach my $using (@$using) - { - @identifiers = NaturalDocs::SymbolString->IdentifiersOf($using); - push @usingStrings, join("\x1F", @identifiers); - }; - - if (!defined $scope) - { $string .= "\x1E"; }; - - $string .= "\x1E" . join("\x1E", @usingStrings); - }; - - return $string; - }; - - -# -# Function: ToBinaryFile -# -# Writes a <ReferenceString> to the passed filehandle. Can also encode an undef. -# -# Parameters: -# -# fileHandle - The filehandle to write to. -# referenceString - The <ReferenceString> to write, or undef. -# binaryFormatFlags - Any <Binary Format Flags> you want to use to influence encoding. -# -# Format: -# -# > [SymbolString: Symbol or undef for an undef reference] -# > [SymbolString: Scope or undef for none] -# > -# > [SymbolString: Using or undef for none] -# > [SymbolString: Using or undef for no more] -# > ... -# > -# > [UInt8: Type unless BINARYREF_NOTYPE is set] -# > [UInt8: Resolving Flags unless BINARYREF_NORESOLVINGFLAGS is set] -# -# Dependencies: -# -# - <ReferenceTypes> must fit into a UInt8. All values must be <= 255. -# - All <Resolving Flags> must fit into a UInt8. All values must be <= 255. -# -sub ToBinaryFile #(fileHandle, referenceString, binaryFormatFlags) - { - my ($self, $fileHandle, $referenceString, $binaryFormatFlags) = @_; - - my ($type, $symbol, $scope, $using, $resolvingFlags) = $self->InformationOf($referenceString); - - # [SymbolString: Symbol or undef for an undef reference] - - NaturalDocs::SymbolString->ToBinaryFile($fileHandle, $symbol); - - # [SymbolString: scope or undef if none] - - NaturalDocs::SymbolString->ToBinaryFile($fileHandle, $scope); - - # [SymbolString: using or undef if none/no more] ... - - if (defined $using) - { - foreach my $usingScope (@$using) - { NaturalDocs::SymbolString->ToBinaryFile($fileHandle, $usingScope); }; - }; - - NaturalDocs::SymbolString->ToBinaryFile($fileHandle, undef); - - # [UInt8: Type unless BINARYREF_NOTYPE is set] - - if (!($binaryFormatFlags & BINARYREF_NOTYPE)) - { print $fileHandle pack('C', $type); }; - - # [UInt8: Resolving Flags unless BINARYREF_NORESOLVINGFLAGS is set] - - if (!($binaryFormatFlags & BINARYREF_NORESOLVINGFLAGS)) - { print $fileHandle pack('C', $type); }; - }; - - -# -# Function: FromBinaryFile -# -# Reads a <ReferenceString> or undef from the passed filehandle. -# -# Parameters: -# -# fileHandle - The filehandle to read from. -# binaryFormatFlags - Any <Binary Format Flags> you want to use to influence decoding. -# type - The <ReferenceType> to use if <BINARYREF_NOTYPE> is set. -# resolvingFlags - The <Resolving Flags> to use if <BINARYREF_NORESOLVINGFLAGS> is set. -# -# Returns: -# -# The <ReferenceString> or undef. -# -# See Also: -# -# See <ToBinaryFile()> for format and dependencies. -# -sub FromBinaryFile #(fileHandle, binaryFormatFlags, type, resolvingFlags) - { - my ($self, $fileHandle, $binaryFormatFlags, $type, $resolvingFlags) = @_; - - # [SymbolString: Symbol or undef for an undef reference] - - my $symbol = NaturalDocs::SymbolString->FromBinaryFile($fileHandle); - - if (!defined $symbol) - { return undef; }; - - # [SymbolString: scope or undef if none] - - my $scope = NaturalDocs::SymbolString->FromBinaryFile($fileHandle); - - # [SymbolString: using or undef if none/no more] ... - - my $usingSymbol; - my @using; - - while ($usingSymbol = NaturalDocs::SymbolString->FromBinaryFile($fileHandle)) - { push @using, $usingSymbol; }; - - if (scalar @using) - { $usingSymbol = \@using; } - else - { $usingSymbol = undef; }; - - # [UInt8: Type unless BINARYREF_NOTYPE is set] - - if (!($binaryFormatFlags & BINARYREF_NOTYPE)) - { - my $raw; - read($fileHandle, $raw, 1); - $type = unpack('C', $raw); - }; - - # [UInt8: Resolving Flags unless BINARYREF_NORESOLVINGFLAGS is set] - - if (!($binaryFormatFlags & BINARYREF_NORESOLVINGFLAGS)) - { - my $raw; - read($fileHandle, $raw, 1); - $resolvingFlags = unpack('C', $raw); - }; - - return $self->MakeFrom($type, $symbol, $scope, $usingSymbol, $resolvingFlags); - }; - - -# -# Function: InformationOf -# -# Returns the information encoded in a <ReferenceString>. -# -# Parameters: -# -# referenceString - The <ReferenceString> to decode. -# -# Returns: -# -# The array ( type, symbol, scope, using, resolvingFlags ). -# -# type - The <ReferenceType>. -# symbol - The <SymbolString>. -# scope - The scope <SymbolString>, or undef if none. -# using - An arrayref of scope <SymbolStrings> that the reference also has access to via "using" statements, or undef if none. -# resolvingFlags - The <Resolving Flags> of the reference. -# -sub InformationOf #(referenceString) - { - my ($self, $referenceString) = @_; - - my ($type, $resolvingFlags, $symbolString, $scopeString, @usingStrings) = split(/\x1E/, $referenceString); - - if (!length $resolvingFlags) - { $resolvingFlags = undef; }; - - my @identifiers = split(/\x1F/, $symbolString); - my $symbol = NaturalDocs::SymbolString->Join(@identifiers); - - my $scope; - if (defined $scopeString && length($scopeString)) - { - @identifiers = split(/\x1F/, $scopeString); - $scope = NaturalDocs::SymbolString->Join(@identifiers); - }; - - my $using; - if (scalar @usingStrings) - { - $using = [ ]; - foreach my $usingString (@usingStrings) - { - @identifiers = split(/\x1F/, $usingString); - push @$using, NaturalDocs::SymbolString->Join(@identifiers); - }; - }; - - return ( $type, $symbol, $scope, $using, $resolvingFlags ); - }; - - -# -# Function: TypeOf -# -# Returns the <ReferenceType> encoded in the reference string. This is faster than <InformationOf()> if this is -# the only information you need. -# -sub TypeOf #(referenceString) - { - my ($self, $referenceString) = @_; - - $referenceString =~ /^([^\x1E]+)/; - return $1; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Settings.pm b/docs/doctool/Modules/NaturalDocs/Settings.pm deleted file mode 100644 index 084c77bc..00000000 --- a/docs/doctool/Modules/NaturalDocs/Settings.pm +++ /dev/null @@ -1,1258 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Settings -# -############################################################################### -# -# A package to handle the command line and various other program settings. -# -# Usage and Dependencies: -# -# - The <Constant Functions> can be called immediately. -# -# - Prior to initialization, <NaturalDocs::Builder> must have all its output packages registered. -# -# - To initialize, call <Load()>. All functions except <InputDirectoryNameOf()> will then be available. -# -# - <GenerateDirectoryNames()> must be called before <InputDirectoryNameOf()> will work. Currently it is called by -# <NaturalDocs::Menu->LoadAndUpdate()>. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - -use Cwd (); - -use NaturalDocs::Settings::BuildTarget; - -use strict; -use integer; - -package NaturalDocs::Settings; - - - -############################################################################### -# Group: Variables - - -# handle: SETTINGSFILEHANDLE -# The file handle used with <Settings.txt>. - -# handle: PREVIOUS_SETTINGS_FILEHANDLE -# The file handle used with <PreviousSettings.nd>. - -# array: inputDirectories -# An array of input directories. -my @inputDirectories; - -# array: inputDirectoryNames -# An array of the input directory names. Each name corresponds to the directory of the same index in <inputDirectories>. -my @inputDirectoryNames; - -# array: excludedInputDirectories -# An array of input directories to exclude. -my @excludedInputDirectories; - -# array: removedInputDirectories -# An array of input directories that were once in the command line but are no longer. -my @removedInputDirectories; - -# array: removedInputDirectoryNames -# An array of the removed input directories names. Each name corresponds to the directory of the same index in -# <removedInputDirectories>. -my @removedInputDirectoryNames; - -# var: projectDirectory -# The project directory. -my $projectDirectory; - -# array: buildTargets -# An array of <NaturalDocs::Settings::BuildTarget>s. -my @buildTargets; - -# var: documentedOnly -# Whether undocumented code aspects should be included in the output. -my $documentedOnly; - -# int: tabLength -# The number of spaces in tabs. -my $tabLength; - -# bool: noAutoGroup -# Whether auto-grouping is turned off. -my $noAutoGroup; - -# bool: isQuiet -# Whether the script should be run in quiet mode or not. -my $isQuiet; - -# array: styles -# An array of style names to use, most important first. -my @styles; - -# var: charset -# The character encoding of the source files, and thus the output. -my $charset; - - -############################################################################### -# Group: Files - -# -# File: Settings.txt -# -# The file that stores the Natural Docs build targets. -# -# Format: -# -# The file is plain text. Blank lines can appear anywhere and are ignored. Tags and their content must be completely -# contained on one line. -# -# > # [comment] -# -# The file supports single-line comments via #. They can appear alone on a line or after content. -# -# > Format: [version] -# > TabLength: [length] -# > Style: [style] -# -# The file format version, tab length, and default style are specified as above. Each can only be specified once, with -# subsequent ones being ignored. Notice that the tags correspond to the long forms of the command line options. -# -# > Source: [directory] -# > Input: [directory] -# -# The input directory is specified as above. As in the command line, either "Source" or "Input" can be used. -# -# > [Extension Option]: [opton] -# -# Options for extensions can be specified as well. The long form is used as the tag. -# -# > Option: [HeadersOnly], [Quiet], [Extension Option] -# -# Options that don't have parameters can be specified in an Option line. The commas are not required. -# -# > Output: [name] -# -# Specifies an output target with a user defined name. The name is what will be referenced from the command line, and the -# name "All" is reserved. -# -# *The options below can only be specified after an output tag.* Everything that follows an output tag is part of that target's -# options until the next output tag. -# -# > Format: [format] -# -# The output format of the target. -# -# > Directory: [directory] -# > Location: [directory] -# > Folder: [directory] -# -# The output directory of the target. All are synonyms. -# -# > Style: [style] -# -# The style of the output target. This overrides the default and is optional. -# - - -# -# File: PreviousSettings.nd -# -# Stores the previous command line settings. -# -# Format: -# -# > [BINARY_FORMAT] -# > [VersionInt: app version] -# -# The file starts with the standard <BINARY_FORMAT> <VersionInt> header. -# -# > [UInt8: tab length] -# > [UInt8: documented only (0 or 1)] -# > [UInt8: no auto-group (0 or 1)] -# > [AString16: charset] -# > -# > [UInt8: number of input directories] -# > [AString16: input directory] [AString16: input directory name] ... -# -# A count of input directories, then that number of directory/name pairs. -# -# > [UInt8: number of output targets] -# > [AString16: output directory] [AString16: output format command line option] ... -# -# A count of output targets, then that number of directory/format pairs. -# -# -# Revisions: -# -# 1.33: -# -# - Added charset. -# -# 1.3: -# -# - Removed headers-only, which was a 0/1 UInt8 after tab length. -# - Change auto-group level (1 = no, 2 = yes, 3 = full only) to no auto-group (0 or 1). -# -# 1.22: -# -# - Added auto-group level. -# -# 1.2: -# -# - File was added to the project. Prior to 1.2, it didn't exist. -# - - -############################################################################### -# Group: Action Functions - -# -# Function: Load -# -# Loads and parses all settings from the command line and configuration files. Will exit if the options are invalid or the syntax -# reference was requested. -# -sub Load - { - my ($self) = @_; - - $self->ParseCommandLine(); - $self->LoadAndComparePreviousSettings(); - }; - - -# -# Function: Save -# -# Saves all settings in configuration files to disk. -# -sub Save - { - my ($self) = @_; - - $self->SavePreviousSettings(); - }; - - -# -# Function: GenerateDirectoryNames -# -# Generates names for each of the input directories, which can later be retrieved with <InputDirectoryNameOf()>. -# -# Parameters: -# -# hints - A hashref of suggested names, where the keys are the directories and the values are the names. These take -# precedence over anything generated. You should include names for directories that are no longer in the command -# line. This parameter may be undef. -# -sub GenerateDirectoryNames #(hints) - { - my ($self, $hints) = @_; - - my %usedNames; - - - if (defined $hints) - { - # First, we have to convert all non-numeric names to numbers, since they may come from a pre-1.32 menu file. We do it - # here instead of in NaturalDocs::Menu to keep the naming scheme centralized. - - my @names = values %$hints; - my $hasNonNumeric; - - foreach my $name (@names) - { - if ($name !~ /^[0-9]+$/) - { - $hasNonNumeric = 1; - last; - }; - }; - - - if ($hasNonNumeric) - { - # Hash mapping old names to new names. - my %conversion; - - # The sequential number to use. Starts at two because we want 'default' to be one. - my $currentNumber = 2; - - # If there's only one name, we set it to one no matter what it was set to before. - if (scalar @names == 1) - { $conversion{$names[0]} = 1; } - else - { - # We sort the list first because we want the end result to be predictable. This conversion could be happening on many - # machines, and they may not all specify the input directories in the same order. They need to all come up with the - # same result. - @names = sort @names; - - foreach my $name (@names) - { - if ($name eq 'default') - { $conversion{$name} = 1; } - else - { - $conversion{$name} = $currentNumber; - $currentNumber++; - }; - }; - }; - - # Convert them to the new names. - foreach my $directory (keys %$hints) - { - $hints->{$directory} = $conversion{ $hints->{$directory} }; - }; - }; - - - # Now we apply all the names from the hints. - - for (my $i = 0; $i < scalar @inputDirectories; $i++) - { - if (exists $hints->{$inputDirectories[$i]}) - { - $inputDirectoryNames[$i] = $hints->{$inputDirectories[$i]}; - $usedNames{ $hints->{$inputDirectories[$i]} } = 1; - delete $hints->{$inputDirectories[$i]}; - }; - }; - - - # Any remaining hints are saved as removed directories. - - while (my ($directory, $name) = each %$hints) - { - push @removedInputDirectories, $directory; - push @removedInputDirectoryNames, $name; - }; - }; - - - # Now we generate names for anything remaining. - - my $nameCounter = 1; - - for (my $i = 0; $i < scalar @inputDirectories; $i++) - { - if (!defined $inputDirectoryNames[$i]) - { - while (exists $usedNames{$nameCounter}) - { $nameCounter++; }; - - $inputDirectoryNames[$i] = $nameCounter; - $usedNames{$nameCounter} = 1; - - $nameCounter++; - }; - }; - }; - - - -############################################################################### -# Group: Information Functions - - -# -# Function: InputDirectories -# -# Returns an arrayref of input directories. Do not change. -# -# This will not return any removed input directories. -# -sub InputDirectories - { return \@inputDirectories; }; - -# -# Function: InputDirectoryNameOf -# -# Returns the generated name of the passed input directory. <GenerateDirectoryNames()> must be called once before this -# function is available. -# -# If a name for a removed input directory is available, it will be returned as well. -# -sub InputDirectoryNameOf #(directory) - { - my ($self, $directory) = @_; - - my $name; - - for (my $i = 0; $i < scalar @inputDirectories && !defined $name; $i++) - { - if ($directory eq $inputDirectories[$i]) - { $name = $inputDirectoryNames[$i]; }; - }; - - for (my $i = 0; $i < scalar @removedInputDirectories && !defined $name; $i++) - { - if ($directory eq $removedInputDirectories[$i]) - { $name = $removedInputDirectoryNames[$i]; }; - }; - - return $name; - }; - - -# -# Function: SplitFromInputDirectory -# -# Takes an input file name and returns the array ( inputDirectory, relativePath ). -# -# If the file cannot be split from an input directory, it will try to do it with the removed input directories. -# -sub SplitFromInputDirectory #(file) - { - my ($self, $file) = @_; - - foreach my $directory (@inputDirectories, @removedInputDirectories) - { - if (NaturalDocs::File->IsSubPathOf($directory, $file)) - { return ( $directory, NaturalDocs::File->MakeRelativePath($directory, $file) ); }; - }; - - return ( ); - }; - -# Function: ExcludedInputDirectories -# Returns an arrayref of input directories to exclude. Do not change. -sub ExcludedInputDirectories - { return \@excludedInputDirectories; }; - -# Function: BuildTargets -# Returns an arrayref of <NaturalDocs::Settings::BuildTarget>s. Do not change. -sub BuildTargets - { return \@buildTargets; }; - -# -# Function: OutputDirectoryOf -# -# Returns the output directory of a builder object. -# -# Parameters: -# -# object - The builder object, whose class is derived from <NaturalDocs::Builder::Base>. -# -# Returns: -# -# The builder directory, or undef if the object wasn't found.. -# -sub OutputDirectoryOf #(object) - { - my ($self, $object) = @_; - - foreach my $buildTarget (@buildTargets) - { - if ($buildTarget->Builder() == $object) - { return $buildTarget->Directory(); }; - }; - - return undef; - }; - - -# Function: Styles -# Returns an arrayref of the styles associated with the output. -sub Styles - { return \@styles; }; - -# Function: ProjectDirectory -# Returns the project directory. -sub ProjectDirectory - { return $projectDirectory; }; - -# Function: ProjectDataDirectory -# Returns the project data directory. -sub ProjectDataDirectory - { return NaturalDocs::File->JoinPaths($projectDirectory, 'Data', 1); }; - -# Function: StyleDirectory -# Returns the main style directory. -sub StyleDirectory - { return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'Styles', 1); }; - -# Function: JavaScriptDirectory -# Returns the main JavaScript directory. -sub JavaScriptDirectory - { return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'JavaScript', 1); }; - -# Function: ConfigDirectory -# Returns the main configuration directory. -sub ConfigDirectory - { return NaturalDocs::File->JoinPaths($FindBin::RealBin, 'Config', 1); }; - -# Function: DocumentedOnly -# Returns whether undocumented code aspects should be included in the output. -sub DocumentedOnly - { return $documentedOnly; }; - -# Function: TabLength -# Returns the number of spaces tabs should be expanded to. -sub TabLength - { return $tabLength; }; - -# Function: NoAutoGroup -# Returns whether auto-grouping is turned off. -sub NoAutoGroup - { return $noAutoGroup; }; - -# Function: IsQuiet -# Returns whether the script should be run in quiet mode or not. -sub IsQuiet - { return $isQuiet; }; - -# Function: CharSet -# Returns the character set, or undef if none. -sub CharSet - { return $charset; }; - - -############################################################################### -# Group: Constant Functions - -# -# Function: AppVersion -# -# Returns Natural Docs' version number as an integer. Use <TextAppVersion()> to get a printable version. -# -sub AppVersion - { - my ($self) = @_; - return NaturalDocs::Version->FromString($self->TextAppVersion()); - }; - -# -# Function: TextAppVersion -# -# Returns Natural Docs' version number as plain text. -# -sub TextAppVersion - { return '1.35'; }; - -# -# Function: AppURL -# -# Returns a string of the project's current web address. -# -sub AppURL - { return 'http://www.naturaldocs.org'; }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: ParseCommandLine -# -# Parses and validates the command line. Will cause the script to exit if the options ask for the syntax reference or -# are invalid. -# -sub ParseCommandLine - { - my ($self) = @_; - - my %synonyms = ( 'input' => '-i', - 'source' => '-i', - 'excludeinput' => '-xi', - 'excludesource' => '-xi', - 'output' => '-o', - 'project' => '-p', - 'documentedonly' => '-do', - 'style' => '-s', - 'rebuild' => '-r', - 'rebuildoutput' => '-ro', - 'tablength' => '-t', - 'quiet' => '-q', - 'headersonly' => '-ho', - 'help' => '-h', - 'autogroup' => '-ag', - 'noautogroup' => '-nag', - 'charset' => '-cs', - 'characterset' => '-cs' ); - - - my @errorMessages; - - my $valueRef; - my $option; - - my @outputStrings; - - - # Sometimes $valueRef is set to $ignored instead of undef because we don't want certain errors to cause other, - # unnecessary errors. For example, if they set the input directory twice, we want to show that error and swallow the - # specified directory without complaint. Otherwise it would complain about the directory too as if it were random crap - # inserted into the command line. - my $ignored; - - my $index = 0; - - while ($index < scalar @ARGV) - { - my $arg = $ARGV[$index]; - - if (substr($arg, 0, 1) eq '-') - { - $option = lc($arg); - - # Support options like -t2 as well as -t 2. - if ($option =~ /^([^0-9]+)([0-9]+)$/) - { - $option = $1; - splice(@ARGV, $index + 1, 0, $2); - }; - - # Convert long forms to short. - if (substr($option, 1, 1) eq '-') - { - # Strip all dashes. - my $newOption = $option; - $newOption =~ tr/-//d; - - if (exists $synonyms{$newOption}) - { $option = $synonyms{$newOption}; } - } - - if ($option eq '-i') - { - push @inputDirectories, undef; - $valueRef = \$inputDirectories[-1]; - } - elsif ($option eq '-xi') - { - push @excludedInputDirectories, undef; - $valueRef = \$excludedInputDirectories[-1]; - } - elsif ($option eq '-p') - { - if (defined $projectDirectory) - { - push @errorMessages, 'You cannot have more than one project directory.'; - $valueRef = \$ignored; - } - else - { $valueRef = \$projectDirectory; }; - } - elsif ($option eq '-o') - { - push @outputStrings, undef; - $valueRef = \$outputStrings[-1]; - } - elsif ($option eq '-s') - { - $valueRef = \$styles[0]; - } - elsif ($option eq '-t') - { - $valueRef = \$tabLength; - } - elsif ($option eq '-cs') - { - $valueRef = \$charset; - } - elsif ($option eq '-ag') - { - push @errorMessages, 'The -ag setting is no longer supported. You can use -nag (--no-auto-group) to turn off ' - . "auto-grouping, but there aren't multiple levels anymore."; - $valueRef = \$ignored; - } - - # Options that aren't followed by content. - else - { - $valueRef = undef; - - if ($option eq '-r') - { - NaturalDocs::Project->ReparseEverything(); - NaturalDocs::Project->RebuildEverything(); - } - elsif ($option eq '-ro') - { - NaturalDocs::Project->RebuildEverything(); - } - elsif ($option eq '-do') - { $documentedOnly = 1; } - elsif ($option eq '-q') - { $isQuiet = 1; } - elsif ($option eq '-ho') - { - push @errorMessages, 'The -ho setting is no longer supported. You can have Natural Docs skip over the source file ' - . 'extensions by editing Languages.txt in your project directory.'; - } - elsif ($option eq '-nag') - { $noAutoGroup = 1; } - elsif ($option eq '-?' || $option eq '-h') - { - $self->PrintSyntax(); - exit; - } - else - { push @errorMessages, 'Unrecognized option ' . $option; }; - - }; - - } - - # Is a segment of text, not an option... - else - { - if (defined $valueRef) - { - # We want to preserve spaces in paths. - if (defined $$valueRef) - { $$valueRef .= ' '; }; - - $$valueRef .= $arg; - } - - else - { - push @errorMessages, 'Unrecognized element ' . $arg; - }; - }; - - $index++; - }; - - - # Validate the style, if specified. - - if ($styles[0]) - { - my @stylePieces = split(/ +/, $styles[0]); - @styles = ( ); - - while (scalar @stylePieces) - { - if (lc($stylePieces[0]) eq 'custom') - { - push @errorMessages, 'The "Custom" style setting is no longer supported. Copy your custom style sheet to your ' - . 'project directory and you can refer to it with -s.'; - shift @stylePieces; - } - else - { - # People may use styles with spaces in them. If a style doesn't exist, we need to join the pieces until we find one that - # does or we run out of pieces. - - my $extras = 0; - my $success; - - while ($extras < scalar @stylePieces) - { - my $style; - - if (!$extras) - { $style = $stylePieces[0]; } - else - { $style = join(' ', @stylePieces[0..$extras]); }; - - my $cssFile = NaturalDocs::File->JoinPaths( $self->StyleDirectory(), $style . '.css' ); - if (-e $cssFile) - { - push @styles, $style; - splice(@stylePieces, 0, 1 + $extras); - $success = 1; - last; - } - else - { - $cssFile = NaturalDocs::File->JoinPaths( $self->ProjectDirectory(), $style . '.css' ); - - if (-e $cssFile) - { - push @styles, $style; - splice(@stylePieces, 0, 1 + $extras); - $success = 1; - last; - } - else - { $extras++; }; - }; - }; - - if (!$success) - { - push @errorMessages, 'The style "' . $stylePieces[0] . '" does not exist.'; - shift @stylePieces; - }; - }; - }; - } - else - { @styles = ( 'Default' ); }; - - - # Decode and validate the output strings. - - my %outputDirectories; - - foreach my $outputString (@outputStrings) - { - my ($format, $directory) = split(/ /, $outputString, 2); - - if (!defined $directory) - { push @errorMessages, 'The -o option needs two parameters: -o [format] [directory]'; } - else - { - if (!NaturalDocs::File->PathIsAbsolute($directory)) - { $directory = NaturalDocs::File->JoinPaths(Cwd::cwd(), $directory, 1); }; - - $directory = NaturalDocs::File->CanonizePath($directory); - - if (! -e $directory || ! -d $directory) - { - # They may have forgotten the format portion and the directory name had a space in it. - if (-e ($format . ' ' . $directory) && -d ($format . ' ' . $directory)) - { - push @errorMessages, 'The -o option needs two parameters: -o [format] [directory]'; - $format = undef; - } - else - { push @errorMessages, 'The output directory ' . $directory . ' does not exist.'; } - } - elsif (exists $outputDirectories{$directory}) - { push @errorMessages, 'You cannot specify the output directory ' . $directory . ' more than once.'; } - else - { $outputDirectories{$directory} = 1; }; - - if (defined $format) - { - my $builderPackage = NaturalDocs::Builder->OutputPackageOf($format); - - if (defined $builderPackage) - { - push @buildTargets, - NaturalDocs::Settings::BuildTarget->New(undef, $builderPackage->New(), $directory); - } - else - { - push @errorMessages, 'The output format ' . $format . ' doesn\'t exist or is not installed.'; - $valueRef = \$ignored; - }; - }; - }; - }; - - if (!scalar @buildTargets) - { push @errorMessages, 'You did not specify an output directory.'; }; - - - # Make sure the input and project directories are specified, canonized, and exist. - - if (scalar @inputDirectories) - { - for (my $i = 0; $i < scalar @inputDirectories; $i++) - { - if (!NaturalDocs::File->PathIsAbsolute($inputDirectories[$i])) - { $inputDirectories[$i] = NaturalDocs::File->JoinPaths(Cwd::cwd(), $inputDirectories[$i], 1); }; - - $inputDirectories[$i] = NaturalDocs::File->CanonizePath($inputDirectories[$i]); - - if (! -e $inputDirectories[$i] || ! -d $inputDirectories[$i]) - { push @errorMessages, 'The input directory ' . $inputDirectories[$i] . ' does not exist.'; }; - }; - } - else - { push @errorMessages, 'You did not specify an input (source) directory.'; }; - - if (defined $projectDirectory) - { - if (!NaturalDocs::File->PathIsAbsolute($projectDirectory)) - { $projectDirectory = NaturalDocs::File->JoinPaths(Cwd::cwd(), $projectDirectory, 1); }; - - $projectDirectory = NaturalDocs::File->CanonizePath($projectDirectory); - - if (! -e $projectDirectory || ! -d $projectDirectory) - { push @errorMessages, 'The project directory ' . $projectDirectory . ' does not exist.'; }; - - # Create the Data subdirectory if it doesn't exist. - NaturalDocs::File->CreatePath( NaturalDocs::File->JoinPaths($projectDirectory, 'Data', 1) ); - } - else - { push @errorMessages, 'You did not specify a project directory.'; }; - - - # Make sure the excluded input directories are canonized, and add the project and output directories to the list. - - for (my $i = 0; $i < scalar @excludedInputDirectories; $i++) - { - if (!NaturalDocs::File->PathIsAbsolute($excludedInputDirectories[$i])) - { $excludedInputDirectories[$i] = NaturalDocs::File->JoinPaths(Cwd::cwd(), $excludedInputDirectories[$i], 1); }; - - $excludedInputDirectories[$i] = NaturalDocs::File->CanonizePath($excludedInputDirectories[$i]); - }; - - push @excludedInputDirectories, $projectDirectory; - - foreach my $buildTarget (@buildTargets) - { - push @excludedInputDirectories, $buildTarget->Directory(); - }; - - - # Determine the tab length, and default to four if not specified. - - if (defined $tabLength) - { - if ($tabLength !~ /^[0-9]+$/) - { push @errorMessages, 'The tab length must be a number.'; }; - } - else - { $tabLength = 4; }; - - - # Strip any quotes off of the charset. - $charset =~ tr/\"//d; - - - # Exit with the error message if there was one. - - if (scalar @errorMessages) - { - print join("\n", @errorMessages) . "\nType NaturalDocs -h to see the syntax reference.\n"; - exit; - }; - }; - -# -# Function: PrintSyntax -# -# Prints the syntax reference. -# -sub PrintSyntax - { - my ($self) = @_; - - # Make sure all line lengths are under 80 characters. - - print - - "Natural Docs, version " . $self->TextAppVersion() . "\n" - . $self->AppURL() . "\n" - . "This program is licensed under the GPL\n" - . "--------------------------------------\n" - . "\n" - . "Syntax:\n" - . "\n" - . " NaturalDocs -i [input (source) directory]\n" - . " (-i [input (source) directory] ...)\n" - . " -o [output format] [output directory]\n" - . " (-o [output format] [output directory] ...)\n" - . " -p [project directory]\n" - . " [options]\n" - . "\n" - . "Examples:\n" - . "\n" - . " NaturalDocs -i C:\\My Project\\Source -o HTML C:\\My Project\\Docs\n" - . " -p C:\\My Project\\Natural Docs\n" - . " NaturalDocs -i /src/project -o HTML /doc/project\n" - . " -p /etc/naturaldocs/project -s Small -q\n" - . "\n" - . "Required Parameters:\n" - . "\n" - . " -i [dir]\n--input [dir]\n--source [dir]\n" - . " Specifies the input (source) directory. Required.\n" - . " Can be specified multiple times.\n" - . "\n" - . " -o [fmt] [dir]\n--output [fmt] [dir]\n" - . " Specifies the output format and directory. Required.\n" - . " Can be specified multiple times, but only once per directory.\n" - . " Possible output formats:\n"; - - $self->PrintOutputFormats(' - '); - - print - "\n" - . " -p [dir]\n--project [dir]\n" - . " Specifies the project directory. Required.\n" - . " There needs to be a unique project directory for every source directory.\n" - . "\n" - . "Optional Parameters:\n" - . "\n" - . " -s [style] ([style] [style] ...)\n--style [style] ([style] [style] ...)\n" - . " Specifies the CSS style when building HTML output. If multiple styles are\n" - . " specified, they will all be included in the order given.\n" - . "\n" - . " -do\n--documented-only\n" - . " Specifies only documented code aspects should be included in the output.\n" - . "\n" - . " -t [len]\n--tab-length [len]\n" - . " Specifies the number of spaces tabs should be expanded to. This only needs\n" - . " to be set if you use tabs in example code and text diagrams. Defaults to 4.\n" - . "\n" - . " -xi [dir]\n--exclude-input [dir]\n--exclude-source [dir]\n" - . " Excludes an input (source) directory from the documentation.\n" - . " Automatically done for the project and output directories. Can\n" - . " be specified multiple times.\n" - . "\n" - . " -nag\n--no-auto-group\n" - . " Turns off auto-grouping completely.\n" - . "\n" - . " -r\n--rebuild\n" - . " Rebuilds all output and data files from scratch.\n" - . " Does not affect the menu file.\n" - . "\n" - . " -ro\n--rebuild-output\n" - . " Rebuilds all output files from scratch.\n" - . "\n" - . " -q\n--quiet\n" - . " Suppresses all non-error output.\n" - . "\n" - . " -?\n -h\n--help\n" - . " Displays this syntax reference.\n"; - }; - - -# -# Function: PrintOutputFormats -# -# Prints all the possible output formats that can be specified with -o. Each one will be placed on its own line. -# -# Parameters: -# -# prefix - Characters to prefix each one with, such as for indentation. -# -sub PrintOutputFormats #(prefix) - { - my ($self, $prefix) = @_; - - my $outputPackages = NaturalDocs::Builder::OutputPackages(); - - foreach my $outputPackage (@$outputPackages) - { - print $prefix . $outputPackage->CommandLineOption() . "\n"; - }; - }; - - -# -# Function: LoadAndComparePreviousSettings -# -# Loads <PreviousSettings.nd> and compares the values there with those in the command line. If differences require it, -# sets <rebuildData> and/or <rebuildOutput>. -# -sub LoadAndComparePreviousSettings - { - my ($self) = @_; - - my $fileIsOkay = 1; - my $fileName = NaturalDocs::Project->PreviousSettingsFile(); - my $version; - - if (!open(PREVIOUS_SETTINGS_FILEHANDLE, '<' . $fileName)) - { $fileIsOkay = undef; } - else - { - # See if it's binary. - binmode(PREVIOUS_SETTINGS_FILEHANDLE); - - my $firstChar; - read(PREVIOUS_SETTINGS_FILEHANDLE, $firstChar, 1); - - if ($firstChar != ::BINARY_FORMAT()) - { - close(PREVIOUS_SETTINGS_FILEHANDLE); - $fileIsOkay = undef; - } - else - { - $version = NaturalDocs::Version->FromBinaryFile(\*PREVIOUS_SETTINGS_FILEHANDLE); - - # The file format changed in 1.33. - - if ($version > NaturalDocs::Settings->AppVersion() || $version < NaturalDocs::Version->FromString('1.33')) - { - close(PREVIOUS_SETTINGS_FILEHANDLE); - $fileIsOkay = undef; - }; - }; - }; - - - if (!$fileIsOkay) - { - # We need to reparse everything because --documented-only may have changed. - # We need to rebuild everything because --tab-length may have changed. - NaturalDocs::Project->ReparseEverything(); - NaturalDocs::Project->RebuildEverything(); - } - else - { - my $raw; - - # [UInt8: tab expansion] - # [UInt8: documented only (0 or 1)] - # [UInt8: no auto-group (0 or 1)] - # [AString16: charset] - - read(PREVIOUS_SETTINGS_FILEHANDLE, $raw, 5); - my ($prevTabLength, $prevDocumentedOnly, $prevNoAutoGroup, $prevCharsetLength) - = unpack('CCCn', $raw); - - if ($prevTabLength != $self->TabLength()) - { - # We need to rebuild all output because this affects all text diagrams. - NaturalDocs::Project->RebuildEverything(); - }; - - if ($prevDocumentedOnly == 0) - { $prevDocumentedOnly = undef; }; - if ($prevNoAutoGroup == 0) - { $prevNoAutoGroup = undef; }; - - if ($prevDocumentedOnly != $self->DocumentedOnly() || - $prevNoAutoGroup != $self->NoAutoGroup()) - { - NaturalDocs::Project->ReparseEverything(); - }; - - my $prevCharset; - read(PREVIOUS_SETTINGS_FILEHANDLE, $prevCharset, $prevCharsetLength); - - if ($prevCharset ne $charset) - { NaturalDocs::Project->RebuildEverything(); }; - - - # [UInt8: number of input directories] - - read(PREVIOUS_SETTINGS_FILEHANDLE, $raw, 1); - my $inputDirectoryCount = unpack('C', $raw); - - while ($inputDirectoryCount) - { - # [AString16: input directory] [AString16: input directory name] ... - - read(PREVIOUS_SETTINGS_FILEHANDLE, $raw, 2); - my $inputDirectoryLength = unpack('n', $raw); - - my $inputDirectory; - read(PREVIOUS_SETTINGS_FILEHANDLE, $inputDirectory, $inputDirectoryLength); - - read (PREVIOUS_SETTINGS_FILEHANDLE, $raw, 2); - my $inputDirectoryNameLength = unpack('n', $raw); - - my $inputDirectoryName; - read(PREVIOUS_SETTINGS_FILEHANDLE, $inputDirectoryName, $inputDirectoryNameLength); - - # Not doing anything with this for now. - - $inputDirectoryCount--; - }; - - - # [UInt8: number of output targets] - - read(PREVIOUS_SETTINGS_FILEHANDLE, $raw, 1); - my $outputTargetCount = unpack('C', $raw); - - # Keys are the directories, values are the command line options. - my %previousOutputDirectories; - - while ($outputTargetCount) - { - # [AString16: output directory] [AString16: output format command line option] ... - - read(PREVIOUS_SETTINGS_FILEHANDLE, $raw, 2); - my $outputDirectoryLength = unpack('n', $raw); - - my $outputDirectory; - read(PREVIOUS_SETTINGS_FILEHANDLE, $outputDirectory, $outputDirectoryLength); - - read (PREVIOUS_SETTINGS_FILEHANDLE, $raw, 2); - my $outputCommandLength = unpack('n', $raw); - - my $outputCommand; - read(PREVIOUS_SETTINGS_FILEHANDLE, $outputCommand, $outputCommandLength); - - $previousOutputDirectories{$outputDirectory} = $outputCommand; - - $outputTargetCount--; - }; - - # Check if any targets were added to the command line, or if their formats changed. We don't care if targets were - # removed. - my $buildTargets = $self->BuildTargets(); - - foreach my $buildTarget (@$buildTargets) - { - if (!exists $previousOutputDirectories{$buildTarget->Directory()} || - $buildTarget->Builder()->CommandLineOption() ne $previousOutputDirectories{$buildTarget->Directory()}) - { - NaturalDocs::Project->RebuildEverything(); - last; - }; - }; - - close(PREVIOUSSTATEFILEHANDLE); - }; - }; - - -# -# Function: SavePreviousSettings -# -# Saves the settings into <PreviousSettings.nd>. -# -sub SavePreviousSettings - { - my ($self) = @_; - - open (PREVIOUS_SETTINGS_FILEHANDLE, '>' . NaturalDocs::Project->PreviousSettingsFile()) - or die "Couldn't save " . NaturalDocs::Project->PreviousSettingsFile() . ".\n"; - - binmode(PREVIOUS_SETTINGS_FILEHANDLE); - - print PREVIOUS_SETTINGS_FILEHANDLE '' . ::BINARY_FORMAT(); - NaturalDocs::Version->ToBinaryFile(\*PREVIOUS_SETTINGS_FILEHANDLE, NaturalDocs::Settings->AppVersion()); - - # [UInt8: tab length] - # [UInt8: documented only (0 or 1)] - # [UInt8: no auto-group (0 or 1)] - # [AString16: charset] - # [UInt8: number of input directories] - - my $inputDirectories = $self->InputDirectories(); - - print PREVIOUS_SETTINGS_FILEHANDLE pack('CCCnA*C', $self->TabLength(), ($self->DocumentedOnly() ? 1 : 0), - ($self->NoAutoGroup() ? 1 : 0), length($charset), $charset, - scalar @$inputDirectories); - - foreach my $inputDirectory (@$inputDirectories) - { - my $inputDirectoryName = $self->InputDirectoryNameOf($inputDirectory); - - # [AString16: input directory] [AString16: input directory name] ... - print PREVIOUS_SETTINGS_FILEHANDLE pack('nA*nA*', length($inputDirectory), $inputDirectory, - length($inputDirectoryName), $inputDirectoryName); - }; - - # [UInt8: number of output targets] - - my $buildTargets = $self->BuildTargets(); - print PREVIOUS_SETTINGS_FILEHANDLE pack('C', scalar @$buildTargets); - - foreach my $buildTarget (@$buildTargets) - { - my $buildTargetDirectory = $buildTarget->Directory(); - my $buildTargetCommand = $buildTarget->Builder()->CommandLineOption(); - - # [AString16: output directory] [AString16: output format command line option] ... - print PREVIOUS_SETTINGS_FILEHANDLE pack('nA*nA*', length($buildTargetDirectory), $buildTargetDirectory, - length($buildTargetCommand), $buildTargetCommand); - }; - - close(PREVIOUS_SETTINGS_FILEHANDLE); - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Settings/BuildTarget.pm b/docs/doctool/Modules/NaturalDocs/Settings/BuildTarget.pm deleted file mode 100644 index 8494a902..00000000 --- a/docs/doctool/Modules/NaturalDocs/Settings/BuildTarget.pm +++ /dev/null @@ -1,91 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::Settings::BuildTarget -# -############################################################################### -# -# A class that stores information about a build target. -# -# Notes: -# -# <Name()> is not used yet. It's present because targets can be named in the settings file ("api", "apipdf", etc.) but the -# settings file isn't implemented yet, so just set it to undef. -# -############################################################################### - -# 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::Settings::BuildTarget; - - -############################################################################### -# Group: Implementation - - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref with the members below. -# -# NAME - The name of the target. -# BUILDER - The <NaturalDocs::Builder::Base>-derived object for the target's output format. -# DIRECTORY - The output directory of the target. -# -use constant NAME => 0; -use constant BUILDER => 1; -use constant DIRECTORY => 2; -# New depends on the order of these constants. - - -############################################################################### -# Group: Functions - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# name - The name of the target. -# builder - The <NaturalDocs::Builder::Base>-derived object for the target's output format. -# directory - The directory to place the output files in. -# -sub New #(name, builder, directory, style) - { - my $package = shift; - - # This depends on the order of the parameters matching the order of the constants. - my $object = [ @_ ]; - bless $object, $package; - - return $object; - }; - - -# Function: Name -# Returns the target's name. -sub Name - { return $_[0]->[NAME]; }; - -# Function: SetName -# Changes the target's name. -sub SetName #(name) - { $_[0]->[NAME] = $_[1]; }; - -# Function: Builder -# Returns the <NaturalDocs::Builder::Base>-derived object for the target's output format. -sub Builder - { return $_[0]->[BUILDER]; }; - -# Function: Directory -# Returns the directory for the traget's output files. -sub Directory - { return $_[0]->[DIRECTORY]; }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/StatusMessage.pm b/docs/doctool/Modules/NaturalDocs/StatusMessage.pm deleted file mode 100644 index 5edb91c2..00000000 --- a/docs/doctool/Modules/NaturalDocs/StatusMessage.pm +++ /dev/null @@ -1,102 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::StatusMessage -# -############################################################################### -# -# A package to handle status message updates. Automatically handles <NaturalDocs::Settings->IsQuiet()>. -# -############################################################################### - -# 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::StatusMessage; - - -# -# var: message -# The message to display. -# -my $message; - -# -# var: total -# The number of items to work through. -# -my $total; - -# -# var: completed -# The number of items completed. -# -my $completed; - -# -# var: lastMessageTime -# The time the last message was posted. -# -my $lastMessageTime; - - -# -# constant: TIME_BETWEEN_UPDATES -# The number of seconds that should occur between updates. -# -use constant TIME_BETWEEN_UPDATES => 10; - - - -# -# Function: Start -# -# Starts the status message. -# -# Parameters: -# -# message - The message to post. -# total - The number of items that are going to be worked through. -# -sub Start #(message, total) - { - my $self = shift; - - if (!NaturalDocs::Settings->IsQuiet()) - { - ($message, $total) = @_; - $completed = 0; - - print $message . "\n"; - - $lastMessageTime = time(); - }; - }; - - -# -# Function: CompletedItem -# -# Should be called every time an item is completed. -# -sub CompletedItem - { - my $self = shift; - - if (!NaturalDocs::Settings->IsQuiet()) - { - # We scale completed by 100 since we need to anyway to get the percentage. - - $completed += 100; - - if (time() >= $lastMessageTime + TIME_BETWEEN_UPDATES) - { - print $message . ' (' . ($completed / $total) . '%)' . "\n"; - $lastMessageTime = time(); - }; - }; - }; - -1; diff --git a/docs/doctool/Modules/NaturalDocs/SymbolString.pm b/docs/doctool/Modules/NaturalDocs/SymbolString.pm deleted file mode 100644 index f573b188..00000000 --- a/docs/doctool/Modules/NaturalDocs/SymbolString.pm +++ /dev/null @@ -1,208 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::SymbolString -# -############################################################################### -# -# A package to manage <SymbolString> handling throughout the program. -# -############################################################################### - -# 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::SymbolString; - - -# -# Function: FromText -# -# Extracts and returns a <SymbolString> from plain text. -# -# This should be the only way to get a <SymbolString> from plain text, as the splitting and normalization must be consistent -# throughout the application. -# -sub FromText #(textSymbol) - { - my ($self, $textSymbol) = @_; - - # The internal format of a symbol is all the normalized identifiers separated by 0x1F characters. - - # Convert whitespace and reserved characters to spaces, and condense multiple consecutive ones. - $textSymbol =~ tr/ \t\r\n\x1C\x1D\x1E\x1F/ /s; - - # Remove spaces unless they're separating two alphanumeric/underscore characters. - $textSymbol =~ s/^ //; - $textSymbol =~ s/ $//; - $textSymbol =~ s/(\W) /$1/g; - $textSymbol =~ s/ (\W)/$1/g; - - # Remove trailing empty parenthesis, so Function and Function() are equivalent. - $textSymbol =~ s/\(\)$//; - - # Split the string into pieces. - my @pieces = split(/(\.+|::|->)/, $textSymbol); - my $symbolString; - - my $lastWasSeparator = 1; - - foreach my $piece (@pieces) - { - if ($piece =~ /^(?:\.|::|->)$/) - { - if (!$lastWasSeparator) - { - $symbolString .= "\x1F"; - $lastWasSeparator = 1; - }; - } - else - { - $symbolString .= $piece; - $lastWasSeparator = 0; - }; - }; - - $symbolString =~ s/\x1F$//; - - return $symbolString; - }; - - -# -# Function: ToText -# -# Converts a <SymbolString> to text, using the passed separator. -# -sub ToText #(symbolString, separator) - { - my ($self, $symbolString, $separator) = @_; - - my @identifiers = $self->IdentifiersOf($symbolString); - return join($separator, @identifiers); - }; - - -# -# Function: ToBinaryFile -# -# Writes a <SymbolString> to the passed filehandle. Can also encode an undef. -# -# Parameters: -# -# fileHandle - The filehandle to write to. -# symbol - The <SymbolString> to write, or undef. -# -# Format: -# -# > [UInt8: number of identifiers] -# > [AString16: identifier] [AString16: identifier] ... -# -# Undef is represented by a zero for the number of identifiers. -# -sub ToBinaryFile #(fileHandle, symbol) - { - my ($self, $fileHandle, $symbol) = @_; - - my @identifiers; - if (defined $symbol) - { @identifiers = $self->IdentifiersOf($symbol); }; - - print $fileHandle pack('C', scalar @identifiers); - - foreach my $identifier (@identifiers) - { - print $fileHandle pack('nA*', length($identifier), $identifier); - }; - }; - - -# -# Function: FromBinaryFile -# -# Loads a <SymbolString> or undef from the filehandle and returns it. -# -# Parameters: -# -# fileHandle - The filehandle to read from. -# -# Returns: -# -# The <SymbolString> or undef. -# -# See also: -# -# See <ToBinaryFile()> for format and dependencies. -# -sub FromBinaryFile #(fileHandle) - { - my ($self, $fileHandle) = @_; - - my $raw; - - # [UInt8: number of identifiers or 0 if none] - - read($fileHandle, $raw, 1); - my $identifierCount = unpack('C', $raw); - - my @identifiers; - - while ($identifierCount) - { - # [AString16: identifier] [AString16: identifier] ... - - read($fileHandle, $raw, 2); - my $identifierLength = unpack('n', $raw); - - my $identifier; - read($fileHandle, $identifier, $identifierLength); - - push @identifiers, $identifier; - - $identifierCount--; - }; - - if (scalar @identifiers) - { return $self->Join(@identifiers); } - else - { return undef; }; - }; - - -# -# Function: IdentifiersOf -# -# Returns the <SymbolString> as an array of identifiers. -# -sub IdentifiersOf #(symbol) - { - my ($self, $symbol) = @_; - return split(/\x1F/, $symbol); - }; - - -# -# Function: Join -# -# Takes a list of identifiers and/or <SymbolStrings> and returns it as a new <SymbolString>. -# -sub Join #(identifier/symbol, identifier/symbol, ...) - { - my ($self, @pieces) = @_; - - # Can't have undefs screwing everything up. - while (scalar @pieces && !defined $pieces[0]) - { shift @pieces; }; - - # We need to test @pieces first because joining on an empty array returns an empty string rather than undef. - if (scalar @pieces) - { return join("\x1F", @pieces); } - else - { return undef; }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/SymbolTable.pm b/docs/doctool/Modules/NaturalDocs/SymbolTable.pm deleted file mode 100644 index f3a5465d..00000000 --- a/docs/doctool/Modules/NaturalDocs/SymbolTable.pm +++ /dev/null @@ -1,1810 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::SymbolTable -# -############################################################################### -# -# A package that handles all the gory details of managing symbols. It handles where they are defined, which files -# reference them, if any are undefined or duplicated, and loading and saving them to a file. -# -# Usage and Dependencies: -# -# - At any time, <RebuildAllIndexes()> can be called. -# -# - <NaturalDocs::Settings>, <NaturalDocs::Languages>, and <NaturalDocs::Project> must be initialized before use. -# -# - <Load()> must be called to initialize the package. At this point, the <Information Functions> will return the symbol -# table as of the last time Natural Docs was run. -# -# - Note that <Load()> and <Save()> only manage <REFERENCE_TEXT> references. All other reference types must be -# managed by their respective classes. They should be readded after <Load()> to recreate the state of the last time -# Natural Docs was run. -# -# - <Purge()> must be called, and then <NaturalDocs::Parser->ParseForInformation()> on all files that have changed so it -# can fully resolve the symbol table via the <Modification Functions>. Afterwards <PurgeResolvingInfo()> can be called -# to reclaim some memory, and the symbol table will reflect the current state of the code. -# -# - <Save()> must be called to commit any changes to the symbol table back to disk. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - - -use NaturalDocs::SymbolTable::Symbol; -use NaturalDocs::SymbolTable::SymbolDefinition; -use NaturalDocs::SymbolTable::Reference; -use NaturalDocs::SymbolTable::File; -use NaturalDocs::SymbolTable::ReferenceTarget; -use NaturalDocs::SymbolTable::IndexElement; - -use strict; -use integer; - -package NaturalDocs::SymbolTable; - - - -############################################################################### -# Group: Variables - -# -# handle: SYMBOLTABLE_FILEHANDLE -# -# The file handle used with <SymbolTable.nd>. -# - -# -# hash: symbols -# -# A hash of all <SymbolStrings>. The keys are the <SymbolStrings> and the values are <NaturalDocs::SymbolTable::Symbol> -# objects. -# -# Prior to <PurgeResolvingInfo()>, both defined symbols and symbols that are merely potential interpretations of references -# will be here. Afterwards, only defined symbols will be here. -# -my %symbols; - -# -# hash: references -# -# A hash of all references in the project. The keys are <ReferenceStrings> and the values are -# <NaturalDocs::SymbolTable::Reference> objects. -# -# Prior to <PurgeResolvingInfo()>, all possible interpretations will be stored for each reference. Afterwards, only the current -# interpretation will be. -# -my %references; - -# -# hash: files -# -# A hash of all the files that define symbols and references in the project. The keys are the <FileNames>, and the values are -# <NaturalDocs::SymbolTable::File> objects. -# -# After <PurgeResolvingInfo()>, this hash will be empty. -# -my %files; - -# -# object: watchedFile -# -# A <NaturalDocs::SymbolTable::File> object of the file being watched for changes. This is compared to the version in <files> -# to see if anything was changed since the last parse. -# -my $watchedFile; - -# -# string: watchedFileName -# -# The <FileName> of the watched file, if any. If there is no watched file, this will be undef. -# -my $watchedFileName; - -# -# hash: watchedFileSymbolDefinitions -# -# A hashref of the symbol definition information for all the <SymbolStrings> in the watched file. The keys are the symbol strings, -# and the values are <NaturalDocs::SymbolTable::SymbolDefinition> objects. -# -my %watchedFileSymbolDefinitions; - - -# -# hash: indexes -# -# A hash of generated symbol indexes. The keys are <TopicTypes> and the values are sorted arrayrefs of -# <NaturalDocs::SymbolTable::IndexElements>, or undef if its empty. -# -my %indexes; - - -# -# hash: indexChanges -# -# A hash of all the indexes that have changed. The keys are the <TopicTypes> and the entries are undef if they have not -# changed, or 1 if they have. The key will not exist if the <TopicType> has not been checked. -# -my %indexChanges; - - -# -# bool: rebuildIndexes -# -# Whether all indexes should be rebuilt regardless of whether they have been changed. -# -my $rebuildIndexes; - - - -############################################################################### -# Group: Files - - -# -# File: SymbolTable.nd -# -# The storage file for the symbol table. -# -# Format: -# -# > [BINARY_FORMAT] -# > [VersionInt: app version] -# -# The file starts with the standard <BINARY_FORMAT> <VersionInt> header. -# -# The first stage of the file is for symbol definitions, analogous to <symbols>. -# -# > [SymbolString: symbol or undef to end] ... -# > -# > [UInt16: number of definitions] -# > -# > [AString16: global definition file] [AString16: TopicType] -# > [AString16: prototype] [AString16: summary] -# > -# > [AString16: definition file] ... -# > -# > ... -# -# These blocks continue until the <SymbolString> is undef. Only defined symbols will be included in this file, so -# number of definitions will never be zero. The first one is always the global definition. If a symbol does not have a -# prototype or summary, the UInt16 length of the string will be zero. -# -# The second stage is for references, which is analogous to <references>. Only <REFERENCE_TEXT> references are -# stored in this file, and their <Resolving Flags> are implied so they aren't stored either. -# -# > [ReferenceString (no type, resolving flags): reference or undef to end] -# > -# > [UInt8: number of definition files] -# > [AString16: definition file] [AString16: definition file] ... -# -# These blocks continue until the <ReferenceString> is undef. Since there can be multiple using <SymbolStrings>, those -# continue until the number of identifiers is zero. Note that all interpretations are rebuilt rather than stored. -# -# See Also: -# -# <File Format Conventions> -# -# Revisions: -# -# 1.3: -# -# - Symbol <TopicTypes> were changed from UInt8s to AString16s, now that <TopicTypes> are strings instead of -# integer constants. -# -# 1.22: -# -# - File format was completely rebuilt to accommodate the new symbol format and to be in binary. To see the plain text -# format prior to 1.22, check out 1.21's version of this file from CVS. It is too big a change to note here. -# - - -############################################################################### -# Group: File Functions - - -# -# Function: Load -# -# Loads the symbol table from disk. -# -sub Load - { - my ($self) = @_; - - my $fileIsOkay; - - if (open(SYMBOLTABLE_FILEHANDLE, '<' . NaturalDocs::Project->SymbolTableFile())) - { - # See if it's binary. - binmode(SYMBOLTABLE_FILEHANDLE); - - my $firstChar; - read(SYMBOLTABLE_FILEHANDLE, $firstChar, 1); - - if ($firstChar == ::BINARY_FORMAT()) - { - my $version = NaturalDocs::Version->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE); - - # 1.3 is incompatible with previous versions. - - if ($version >= NaturalDocs::Version->FromString('1.3') && $version <= NaturalDocs::Settings->AppVersion()) - { $fileIsOkay = 1; } - else - { close(PREVIOUSSTATEFILEHANDLE); }; - } - - else - { close(SYMBOLTABLE_FILEHANDLE); }; - }; - - - if (!$fileIsOkay) - { - NaturalDocs::Project->ReparseEverything(); - return; - } - - my $raw; - - - # Symbols - - for (;;) - { - # [SymbolString: symbol or undef to end] - - my $symbol = NaturalDocs::SymbolString->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE); - - if (!defined $symbol) - { last; }; - - my $symbolObject = NaturalDocs::SymbolTable::Symbol->New(); - $symbols{$symbol} = $symbolObject; - - # [UInt16: number of definitions] - - read(SYMBOLTABLE_FILEHANDLE, $raw, 2); - my $definitionCount = unpack('n', $raw); - - do - { - # [AString16: (global?) definition file] - - read(SYMBOLTABLE_FILEHANDLE, $raw, 2); - my $fileLength = unpack('n', $raw); - - my $file; - read(SYMBOLTABLE_FILEHANDLE, $file, $fileLength); - - # [AString16: TopicType] - - read(SYMBOLTABLE_FILEHANDLE, $raw, 2); - my $typeLength = unpack('n', $raw); - - my $type; - read(SYMBOLTABLE_FILEHANDLE, $type, $typeLength); - - # [AString16: prototype] - - read(SYMBOLTABLE_FILEHANDLE, $raw, 2); - my $prototypeLength = unpack('n', $raw); - - my $prototype; - if ($prototypeLength) - { read(SYMBOLTABLE_FILEHANDLE, $prototype, $prototypeLength); }; - - # [AString16: summary] - - read(SYMBOLTABLE_FILEHANDLE, $raw, 2); - my $summaryLength = unpack('n', $raw); - - my $summary; - if ($summaryLength) - { read(SYMBOLTABLE_FILEHANDLE, $summary, $summaryLength); }; - - $symbolObject->AddDefinition($file, $type, $prototype, $summary); - - # Add it. - - if (!exists $files{$file}) - { $files{$file} = NaturalDocs::SymbolTable::File->New(); }; - - $files{$file}->AddSymbol($symbol); - - $definitionCount--; - } - while ($definitionCount); - }; - - - # References - - for (;;) - { - # [ReferenceString (no type, resolving flags): reference or undef to end] - - my $referenceString = NaturalDocs::ReferenceString->FromBinaryFile(\*SYMBOLTABLE_FILEHANDLE, - ::BINARYREF_NOTYPE() | - ::BINARYREF_NORESOLVINGFLAGS(), - ::REFERENCE_TEXT(), undef); - - if (!defined $referenceString) - { last; }; - - my $referenceObject = NaturalDocs::SymbolTable::Reference->New(); - $references{$referenceString} = $referenceObject; - - # [UInt8: number of definition files] - - read(SYMBOLTABLE_FILEHANDLE, $raw, 1); - my $definitionCount = unpack('C', $raw); - do - { - # [AString16: definition file] [AString16: definition file] ... - - read(SYMBOLTABLE_FILEHANDLE, $raw, 2); - my $definitionLength = unpack('n', $raw); - - my $definition; - read(SYMBOLTABLE_FILEHANDLE, $definition, $definitionLength); - - # Add it. - - $referenceObject->AddDefinition($definition); - - if (!exists $files{$definition}) - { $files{$definition} = NaturalDocs::SymbolTable::File->New(); }; - - $files{$definition}->AddReference($referenceString); - - $definitionCount--; - } - while ($definitionCount); - - $self->GenerateInterpretations($referenceString); - $self->InterpretReference($referenceString); - }; - - close(SYMBOLTABLE_FILEHANDLE); - }; - - -# -# Function: Purge -# -# Purges the symbol table of all symbols and references from files that no longer have Natural Docs content. -# -sub Purge - { - my ($self) = @_; - - my $filesToPurge = NaturalDocs::Project->FilesToPurge(); - - # We do this in two stages. First we delete all the references, and then we delete all the definitions. This causes us to go - # through the list twice, but it makes sure no purged files get added to the build list. For example, if we deleted all of - # Purge File A's references and definitions, and Purge File B had a reference to one of those symbols, Purge File B - # would be added to the build list because one of its references changed. By removing all the references in all the files - # before removing the definitions, we avoid this. - - foreach my $file (keys %$filesToPurge) - { - if (exists $files{$file}) - { - my @references = $files{$file}->References(); - foreach my $reference (@references) - { $self->DeleteReference($reference, $file); }; - }; - }; - - foreach my $file (keys %$filesToPurge) - { - if (exists $files{$file}) - { - my @symbols = $files{$file}->Symbols(); - foreach my $symbol (@symbols) - { $self->DeleteSymbol($symbol, $file); }; - - delete $files{$file}; - }; - }; - }; - - -# -# Function: Save -# -# Saves the symbol table to disk. It is written to <SymbolTable.nd>. -# -sub Save - { - my ($self) = @_; - - open (SYMBOLTABLE_FILEHANDLE, '>' . NaturalDocs::Project->SymbolTableFile()) - or die "Couldn't save " . NaturalDocs::Project->SymbolTableFile() . ".\n"; - - binmode(SYMBOLTABLE_FILEHANDLE); - - print SYMBOLTABLE_FILEHANDLE '' . ::BINARY_FORMAT(); - - NaturalDocs::Version->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, NaturalDocs::Settings->AppVersion()); - - - # Symbols - - while (my ($symbol, $symbolObject) = each %symbols) - { - # Only existing symbols. - if ($symbolObject->IsDefined()) - { - # [SymbolString: symbol or undef to end] - - NaturalDocs::SymbolString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, $symbol); - - # [UInt16: number of definitions] - - my @definitions = $symbolObject->Definitions(); - print SYMBOLTABLE_FILEHANDLE pack('n', scalar @definitions); - - # [AString16: global definition file] [AString16: TopicType] - - print SYMBOLTABLE_FILEHANDLE pack('nA*nA*', length $symbolObject->GlobalDefinition(), - $symbolObject->GlobalDefinition(), - length $symbolObject->GlobalType(), - $symbolObject->GlobalType()); - - # [AString16: prototype] - - my $prototype = $symbolObject->GlobalPrototype(); - - if (defined $prototype) - { print SYMBOLTABLE_FILEHANDLE pack('nA*', length($prototype), $prototype); } - else - { print SYMBOLTABLE_FILEHANDLE pack('n', 0); }; - - # [AString16: summary] - - my $summary = $symbolObject->GlobalSummary(); - - if (defined $summary) - { print SYMBOLTABLE_FILEHANDLE pack('nA*', length($summary), $summary); } - else - { print SYMBOLTABLE_FILEHANDLE pack('n', 0); }; - - - foreach my $definition (@definitions) - { - if ($definition ne $symbolObject->GlobalDefinition()) - { - # [AString16: definition file] [AString16: TopicType] - - print SYMBOLTABLE_FILEHANDLE pack('nA*nA*', length $definition, $definition, - length $symbolObject->TypeDefinedIn($definition), - $symbolObject->TypeDefinedIn($definition)); - - # [AString16: prototype] - - my $prototype = $symbolObject->PrototypeDefinedIn($definition); - - if (defined $prototype) - { print SYMBOLTABLE_FILEHANDLE pack('nA*', length($prototype), $prototype); } - else - { print SYMBOLTABLE_FILEHANDLE pack('n', 0); }; - - # [AString16: summary] - - my $summary = $symbolObject->SummaryDefinedIn($definition); - - if (defined $summary) - { print SYMBOLTABLE_FILEHANDLE pack('nA*', length($summary), $summary); } - else - { print SYMBOLTABLE_FILEHANDLE pack('n', 0); }; - }; - }; - }; - }; - - # [SymbolString: symbol or undef to end] - - NaturalDocs::SymbolString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, undef); - - - # References - - while (my ($reference, $referenceObject) = each %references) - { - my $type = NaturalDocs::ReferenceString->TypeOf($reference); - - if ($type == ::REFERENCE_TEXT()) - { - # [ReferenceString (no type, resolving flags): reference or undef to end] - - NaturalDocs::ReferenceString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, $reference, - ::BINARYREF_NOTYPE() | ::BINARYREF_NORESOLVINGFLAGS()); - - # [UInt8: number of definition files] - - my @definitions = $referenceObject->Definitions(); - print SYMBOLTABLE_FILEHANDLE pack('C', scalar @definitions); - - # [AString16: definition file] [AString16: definition file] ... - - foreach my $definition (@definitions) - { - print SYMBOLTABLE_FILEHANDLE pack('nA*', length($definition), $definition); - }; - }; - }; - - # [ReferenceString (no type, resolving flags): reference or undef to end] - - NaturalDocs::ReferenceString->ToBinaryFile(\*SYMBOLTABLE_FILEHANDLE, undef, - ::BINARYREF_NOTYPE() | ::BINARYREF_NORESOLVINGFLAGS()); - - close(SYMBOLTABLE_FILEHANDLE); - }; - - - -############################################################################### -# Group: Modification Functions -# These functions should not be called after <PurgeResolvingInfo()>. - -# -# Function: AddSymbol -# -# Adds a symbol definition to the table, if it doesn't already exist. If the definition changes or otherwise requires the files that -# reference it to be updated, the function will call <NaturalDocs::Project->RebuildFile()> to make sure that they are. -# -# Parameters: -# -# symbol - The <SymbolString>. -# file - The <FileName> where it's defined. -# type - The symbol's <TopicType>. -# prototype - The symbol's prototype, if applicable. -# summary - The symbol's summary, if applicable. -# -sub AddSymbol #(symbol, file, type, prototype, summary) - { - my ($self, $symbol, $file, $type, $prototype, $summary) = @_; - - - # If the symbol doesn't exist... - if (!exists $symbols{$symbol}) - { - # Create the symbol. There are no references that could be interpreted as this or else it would have existed already. - - my $newSymbol = NaturalDocs::SymbolTable::Symbol->New(); - $newSymbol->AddDefinition($file, $type, $prototype, $summary); - - $symbols{$symbol} = $newSymbol; - - $self->OnIndexChange($type); - NaturalDocs::Project->RebuildFile($file); - } - - - # If the symbol already exists... - else - { - my $symbolObject = $symbols{$symbol}; - - # If the symbol isn't defined, i.e. it was a potential interpretation only... - if (!$symbolObject->IsDefined()) - { - $symbolObject->AddDefinition($file, $type, $prototype, $summary); - - # See if this symbol provides a better interpretation of any references. We can assume this symbol has interpretations - # because the object won't exist without either that or definitions. - - my %referencesAndScores = $symbolObject->ReferencesAndScores(); - - while (my ($referenceString, $referenceScore) = each %referencesAndScores) - { - my $referenceObject = $references{$referenceString}; - - if (!$referenceObject->HasCurrentInterpretation() || - $referenceScore > $referenceObject->CurrentScore()) - { - $referenceObject->SetCurrentInterpretation($symbol); - $self->OnInterpretationChange($referenceString); - }; - }; - - $self->OnIndexChange($type); - NaturalDocs::Project->RebuildFile($file); - } - - # If the symbol is defined but not in this file... - elsif (!$symbolObject->IsDefinedIn($file)) - { - $symbolObject->AddDefinition($file, $type, $prototype, $summary); - - $self->OnIndexChange($type); - NaturalDocs::Project->RebuildFile($file); - - # We don't have to check other files because if the symbol is defined it already has a global definiton, - # and everything else is either using that or its own definition, and thus wouldn't be affected by this. - }; - - # If the symbol was already defined in this file, ignore it. - - }; - - - # Add it to the file index. - - if (!exists $files{$file}) - { $files{$file} = NaturalDocs::SymbolTable::File->New(); }; - - $files{$file}->AddSymbol($symbol); - - - # Add it to the watched file, if necessary. - - if (defined $watchedFileName) - { - $watchedFile->AddSymbol($symbol); - - if (!exists $watchedFileSymbolDefinitions{$symbol}) - { - $watchedFileSymbolDefinitions{$symbol} = - NaturalDocs::SymbolTable::SymbolDefinition->New($type, $prototype, $summary); - }; - }; - }; - - -# -# Function: AddReference -# -# Adds a reference to the table, if it doesn't already exist. -# -# Parameters: -# -# type - The <ReferenceType>. -# symbol - The reference <SymbolString>. -# scope - The scope <SymbolString> it appears in. -# using - An arrayref of scope <SymbolStrings> accessible to the reference via "using" statements, or undef if none. -# file - The <FileName> where the reference appears. This is not required unless the type is <REFERENCE_TEXT>. -# resolvingFlags - The <Resolving Flags> of the reference. They will be ignored if the type is <REFERENCE_TEXT>. -# -# Alternate Parameters: -# -# referenceString - The <ReferenceString> to add. -# file - The <FileName> where the reference appears. This is not required unless the type is <REFERENCE_TEXT>. -# -sub AddReference #(type, symbol, scope, using, file, resolvingFlags) or (referenceString, file) - { - my ($self, $referenceString, $file); - - if (scalar @_ <= 3) - { - ($self, $referenceString, $file) = @_; - } - else - { - my ($type, $symbol, $scope, $using, $resolvingFlags); - ($self, $type, $symbol, $scope, $using, $file, $resolvingFlags) = @_; - - $referenceString = NaturalDocs::ReferenceString->MakeFrom($type, $symbol, $scope, $using, $resolvingFlags); - }; - - - # If the reference doesn't exist... - if (!exists $references{$referenceString}) - { - my $referenceObject = NaturalDocs::SymbolTable::Reference->New(); - - $references{$referenceString} = $referenceObject; - - $self->GenerateInterpretations($referenceString); - $self->InterpretReference($referenceString); - } - - - if (defined $file) - { - $references{$referenceString}->AddDefinition($file); - - - # Add it to the file index. - - if (!exists $files{$file}) - { $files{$file} = NaturalDocs::SymbolTable::File->New(); }; - - $files{$file}->AddReference($referenceString); - - - # Add it to the watched file, if necessary. - - if (defined $watchedFileName) - { $watchedFile->AddReference($referenceString); }; - }; - }; - - -# -# Function: WatchFileForChanges -# -# Tracks a file to see if any symbols or references were changed or deleted in ways that would require other files to be rebuilt. -# Assumes that after this function call, the entire file will be parsed again, and thus every symbol and reference will go through -# <AddSymbol()> and <AddReference()>. Afterwards, call <AnalyzeChanges()> to handle any differences. -# -# Parameters: -# -# file - The <FileName> to watch. -# -sub WatchFileForChanges #(file) - { - my ($self, $file) = @_; - - $watchedFile = NaturalDocs::SymbolTable::File->New(); - $watchedFileName = $file; - %watchedFileSymbolDefinitions = ( ); - }; - - -# -# Function: AnalyzeChanges -# -# Handles any changes found when reparsing a file using <WatchFileForChanges()>. -# -sub AnalyzeChanges - { - my ($self) = @_; - - if (exists $files{$watchedFileName}) - { - - # Go through the references and remove any that were deleted. Ones that were added will have already been added to - # the table in AddReference(). - - my @references = $files{$watchedFileName}->References(); - foreach my $reference (@references) - { - if (!$watchedFile->DefinesReference($reference)) - { $self->DeleteReference($reference, $watchedFileName); }; - }; - }; - - # We have to check if the watched file exists again because DeleteReference() could have removed it. I'm still not sure how a - # file could have references without symbols, but apparently it's happened in the real world because it's crashed on people. - if (exists $files{$watchedFileName}) - { - # Go through the symbols. - - my $rebuildFile; - - my @symbols = $files{$watchedFileName}->Symbols(); - foreach my $symbol (@symbols) - { - # Delete symbols that don't exist. - - if (!$watchedFile->DefinesSymbol($symbol)) - { - $self->DeleteSymbol($symbol, $watchedFileName); - $rebuildFile = 1; - } - - else - { - my $symbolObject = $symbols{$symbol}; - my $newSymbolDef = $watchedFileSymbolDefinitions{$symbol}; - - # Update symbols that changed. - - if ( $symbolObject->TypeDefinedIn($watchedFileName) ne $newSymbolDef->Type() || - $symbolObject->PrototypeDefinedIn($watchedFileName) ne $newSymbolDef->Prototype() || - $symbolObject->SummaryDefinedIn($watchedFileName) ne $newSymbolDef->Summary() ) - { - $self->OnIndexChange($symbolObject->TypeDefinedIn($watchedFileName)); - $self->OnIndexChange($newSymbolDef->Type()); - $rebuildFile = 1; - - $symbolObject->ChangeDefinition($watchedFileName, $newSymbolDef->Type(), $newSymbolDef->Prototype(), - $newSymbolDef->Summary()); - - # If the symbol definition was the global one, we need to update all files that reference it. If it wasn't, the only file - # that could references it is itself, and the only way the symbol definition could change in the first place was if it was - # itself changed. - if ($symbolObject->GlobalDefinition() eq $watchedFileName) - { - # Rebuild the files that have references to this symbol - my @references = $symbolObject->References(); - foreach my $reference (@references) - { - if ($references{$reference}->CurrentInterpretation() eq $symbol) - { $self->OnTargetSymbolChange($reference); }; - }; # While references - }; # If global definition is watched file - }; # If the symbol definition changed - }; # If the symbol still exists - }; # foreach symbol in watched file - - if ($rebuildFile) - { NaturalDocs::Project->RebuildFile($watchedFileName); }; - - }; - - - $watchedFile = undef; - $watchedFileName = undef; - %watchedFileSymbolDefinitions = ( ); - }; - - -# -# Function: DeleteReference -# -# Deletes a reference from the table. -# -# Be careful with this function, as deleting a reference means there are no more of them in the file at all. The tables do not -# keep track of how many times references appear in a file. In these cases you should instead call <WatchFileForChanges()>, -# reparse the file, thus readding all the references, and call <AnalyzeChanges()>. -# -# <REFERENCE_TEXT> references should *always* be managed with <WatchFileForChanges()> and <AnalyzeChanges()>. -# This function should only be used externally for other types of references. -# -# Parameters: -# -# referenceString - The <ReferenceString>. -# file - The <FileName> where the reference is. This is not required unless the type is <REFERENCE_TEXT>. -# -sub DeleteReference #(referenceString, file) - { - my ($self, $referenceString, $file) = @_; - - - # If the reference exists... - if (exists $references{$referenceString}) - { - my $referenceObject = $references{$referenceString}; - - if (defined $file) - { $referenceObject->DeleteDefinition($file); }; - - # If there are no other definitions, or it doesn't use file definitions to begin with... - if (!$referenceObject->IsDefined()) - { - my @interpretations = $referenceObject->Interpretations(); - foreach my $interpretation (@interpretations) - { - $symbols{$interpretation}->DeleteReference($referenceString); - }; - - delete $references{$referenceString}; - }; - - - if (defined $file) - { - # Remove it from the file index. - - $files{$file}->DeleteReference($referenceString); - - if (!$files{$file}->HasAnything()) - { delete $files{$file}; }; - - # We don't need to worry about the watched file, since this function will only be called by AnalyzeChanges() and - # LoadAndPurge(). - }; - }; - }; - - -# -# Function: RebuildAllIndexes -# -# When called, it makes sure all indexes are listed as changed by <IndexChanged()>, regardless of whether they actually did -# or not. -# -# This can be called at any time. -# -sub RebuildAllIndexes - { - my $self = shift; - $rebuildIndexes = 1; - }; - - -# -# Function: PurgeResolvingInfo -# -# Purges unnecessary information from the symbol table after it is fully resolved. This will reduce the memory footprint for the -# build stage. After calling this function, you can only call the <Information Functions> and <Save()>. -# -sub PurgeResolvingInfo - { - my ($self) = @_; - - # Go through the symbols. We don't need to keep around potential symbols anymore, nor do we need what references can - # be interpreted as the defined ones. - - while (my ($symbol, $symbolObject) = each %symbols) - { - if ($symbolObject->IsDefined()) - { $symbolObject->DeleteAllReferences(); } - else - { delete $symbols{$symbol}; }; - }; - - - # Go through the references. We don't need any of the interpretations except for the current. - - foreach my $referenceObject (values %references) - { $referenceObject->DeleteAllInterpretationsButCurrent(); }; - - - # We don't need the information by file at all. - - %files = ( ); - }; - - -# -# Function: PurgeIndexes -# -# Clears all generated indexes. -# -sub PurgeIndexes - { - my ($self) = @_; - %indexes = ( ); - }; - - -############################################################################### -# Group: Information Functions -# These functions should not be called until the symbol table is fully resolved. - - -# -# Function: References -# -# Returns what the passed reference information resolve to, if anything. Note that this only works if the reference had -# been previously added to the table via <AddReference()> with the exact same parameters. -# -# Parameters: -# -# type - The <ReferenceType>. -# symbol - The reference <SymbolString>. -# scope - The scope <SymbolString> the reference appears in, or undef if none. -# using - An arrayref of scope <SymbolStrings> available to the reference via using statements. -# file - The source <FileName> the reference appears in, or undef if none. -# resolvingFlags - The <Resolving Flags> of the reference. Ignored if the type is <REFERENCE_TEXT>. -# -# Alternate Parameters: -# -# referenceString - The <ReferenceString> to resolve. -# file - The source <FileName> the reference appears in, or undef if none. -# -# Returns: -# -# A <NaturalDocs::SymbolTable::ReferenceTarget> object, or undef if the reference doesn't resolve to anything. -# -sub References #(type, symbol, scope, using, file, resolvingFlags) or (referenceString, file) - { - my ($self, $referenceString, $file); - - if (scalar @_ <= 3) - { ($self, $referenceString, $file) = @_; } - else - { - my ($type, $symbol, $scope, $using, $resolvingFlags); - ($self, $type, $symbol, $scope, $using, $file, $resolvingFlags) = @_; - - $referenceString = NaturalDocs::ReferenceString->MakeFrom($type, $symbol, $scope, $using, $resolvingFlags); - }; - - if (exists $references{$referenceString} && $references{$referenceString}->HasCurrentInterpretation()) - { - my $targetSymbol = $references{$referenceString}->CurrentInterpretation(); - my $targetObject = $symbols{$targetSymbol}; - - my $targetFile; - my $targetType; - my $targetPrototype; - my $targetSummary; - - if (defined $file && $targetObject->IsDefinedIn($file)) - { - $targetFile = $file; - $targetType = $targetObject->TypeDefinedIn($file); - $targetPrototype = $targetObject->PrototypeDefinedIn($file); - $targetSummary = $targetObject->SummaryDefinedIn($file); - } - else - { - $targetFile = $targetObject->GlobalDefinition(); - $targetType = $targetObject->GlobalType(); - $targetPrototype = $targetObject->GlobalPrototype(); - $targetSummary = $targetObject->GlobalSummary(); - }; - - return NaturalDocs::SymbolTable::ReferenceTarget->New($targetSymbol, $targetFile, $targetType, $targetPrototype, - $targetSummary); - } - - else - { return undef; }; - }; - - -# -# Function: Lookup -# -# Returns information on the passed <SymbolString>, if it exists. Note that the symbol must be fully resolved. -# -# Parameters: -# -# symbol - The <SymbolString>. -# file - The source <FileName> the reference appears in, or undef if none. -# -# Returns: -# -# A <NaturalDocs::SymbolTable::ReferenceTarget> object, or undef if the symbol isn't defined. -# -sub Lookup #(symbol, file) - { - my ($self, $symbol, $file) = @_; - - my $symbolObject = $symbols{$symbol}; - - if (defined $symbolObject) - { - my $targetFile; - my $targetType; - my $targetPrototype; - my $targetSummary; - - if (defined $file && $symbolObject->IsDefinedIn($file)) - { - $targetFile = $file; - $targetType = $symbolObject->TypeDefinedIn($file); - $targetPrototype = $symbolObject->PrototypeDefinedIn($file); - $targetSummary = $symbolObject->SummaryDefinedIn($file); - } - else - { - $targetFile = $symbolObject->GlobalDefinition(); - $targetType = $symbolObject->GlobalType(); - $targetPrototype = $symbolObject->GlobalPrototype(); - $targetSummary = $symbolObject->GlobalSummary(); - }; - - return NaturalDocs::SymbolTable::ReferenceTarget->New($symbol, $targetFile, $targetType, $targetPrototype, - $targetSummary); - } - - else - { return undef; }; - }; - - -# -# Function: Index -# -# Returns a symbol index. -# -# Indexes are generated on demand, but they are stored so subsequent calls for the same index will be fast. Call -# <PurgeIndexes()> to clear the generated indexes. -# -# Parameters: -# -# type - The <TopicType> of symbol to limit the index to, or undef for none. -# -# Returns: -# -# An arrayref of sections. The first represents all the symbols, the second the numbers, and the rest A through Z. -# Each section is a sorted arrayref of <NaturalDocs::SymbolTable::IndexElement> objects. If a section has no content, -# it will be undef. -# -sub Index #(type) - { - my ($self, $type) = @_; - - if (!exists $indexes{$type}) - { $indexes{$type} = $self->MakeIndex($type); }; - - return $indexes{$type}; - }; - - -# -# Function: HasIndexes -# -# Determines which indexes out of a list actually have content. -# -# Parameters: -# -# types - An existence hashref of the <TopicTypes> to check for indexes. -# -# Returns: -# -# An existence hashref of all the specified indexes that have content. Will return an empty hashref if none. -# -sub HasIndexes #(types) - { - my ($self, $types) = @_; - - # EliminationHash is a copy of all the types, and the types will be deleted as they are found. This allows us to quit early if - # we've found all the types because the hash will be empty. We'll later return the original hash minus what was left over - # in here, which are the ones that weren't found. - my %eliminationHash = %$types; - - finddefs: - foreach my $symbolObject (values %symbols) - { - foreach my $definition ($symbolObject->Definitions()) - { - delete $eliminationHash{ $symbolObject->TypeDefinedIn($definition) }; - delete $eliminationHash{ ::TOPIC_GENERAL() }; - - if (!scalar keys %eliminationHash) - { last finddefs; }; - }; - }; - - my $result = { %$types }; - - foreach my $type (keys %eliminationHash) - { delete $result->{$type}; }; - - return $result; - }; - -# -# Function: IndexChanged -# -# Returns whether the specified index has changed. -# -# Parameters: -# -# type - The <TopicType> to limit the index to. -# -sub IndexChanged #(type) - { - my ($self, $type) = @_; - return ($rebuildIndexes || defined $indexChanges{$type}); - }; - - - -############################################################################### -# Group: Event Handlers - - -# -# Function: OnIndexChange -# -# Called whenever a change happens to a symbol that would cause an index to be regenerated. -# -# Parameters: -# -# type - The <TopicType> of the symbol that caused the change. -# -sub OnIndexChange #(type) - { - my ($self, $type) = @_; - - $indexChanges{$type} = 1; - $indexChanges{::TOPIC_GENERAL()} = 1; - }; - - -# -# Function: OnInterpretationChange -# -# Called whenever the current interpretation of a reference changes, meaning it switched from one symbol to another. -# -# Parameters: -# -# referenceString - The <ReferenceString> whose current interpretation changed. -# -sub OnInterpretationChange #(referenceString) - { - my ($self, $referenceString) = @_; - my $referenceType = NaturalDocs::ReferenceString->TypeOf($referenceString); - - if ($referenceType == ::REFERENCE_TEXT()) - { - my @referenceDefinitions = $references{$referenceString}->Definitions(); - - foreach my $referenceDefinition (@referenceDefinitions) - { - NaturalDocs::Project->RebuildFile($referenceDefinition); - }; - } - - elsif (NaturalDocs::Constants->IsClassHierarchyReference($referenceType)) - { - NaturalDocs::ClassHierarchy->OnInterpretationChange($referenceString); - }; - }; - - -# -# Function: OnTargetSymbolChange -# -# Called whenever the symbol that serves as the interpretation of a reference changes, but the reference still resolves to -# the same symbol. This would happen if the type, prototype, summary, or which file serves as global definition of the symbol -# changes. -# -# Parameters: -# -# referenceString - The <ReferenceString> whose interpretation's symbol changed. -# -sub OnTargetSymbolChange #(referenceString) - { - my ($self, $referenceString) = @_; - my $referenceType = NaturalDocs::ReferenceString->TypeOf($referenceString); - - if ($referenceType == ::REFERENCE_TEXT()) - { - my @referenceDefinitions = $references{$referenceString}->Definitions(); - - foreach my $referenceDefinition (@referenceDefinitions) - { - NaturalDocs::Project->RebuildFile($referenceDefinition); - }; - } - - elsif (NaturalDocs::Constants->IsClassHierarchyReference($referenceType)) - { - NaturalDocs::ClassHierarchy->OnTargetSymbolChange($referenceString); - }; - }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: DeleteSymbol -# -# Removes a symbol definition from the table. It will call <OnInterpretationChange()> for all references that have it as their -# current interpretation. -# -# External code should not attempt to delete symbols using this function. Instead it should call <WatchFileFoChanges()>, -# reparse the file, and call <AnalyzeChanges()>. -# -# Parameters: -# -# symbol - The <SymbolString>. -# file - The <FileName> where the definition is. -# -sub DeleteSymbol #(symbol, file) - { - my ($self, $symbol, $file) = @_; - - - # If the symbol and definition exist... - if (exists $symbols{$symbol} && $symbols{$symbol}->IsDefinedIn($file)) - { - my $symbolObject = $symbols{$symbol}; - my $wasGlobal = ($symbolObject->GlobalDefinition() eq $file); - - $self->OnIndexChange($symbolObject->TypeDefinedIn($file)); - - $symbolObject->DeleteDefinition($file); - - # If this was one definition of many... - if ($symbolObject->IsDefined()) - { - - # If this was the global definition... - if ($wasGlobal) - { - # Update every file that referenced the global symbol; i.e. every file that doesn't have its own definition. - - my @references = $symbolObject->References(); - - foreach my $reference (@references) - { - if ($references{$reference}->CurrentInterpretation() eq $symbol) - { - $self->OnTargetSymbolChange($reference); - }; - }; - } - - # If this wasn't the global definition... - else - { - # It's a safe bet that we don't need to do anything here. The only thing that we even need to look for here is if the - # file referenced its own symbol and thus should be rebuilt. However, if the file is having a symbol deleted, it either - # changed or was itself deleted. If it changed and still has other Natural Docs content, it should already be on the - # rebuild list. If it was deleted or no longer has Natural Docs content, we certainly don't want to add it to the rebuild - # list. - }; - } - - # If this is the only definition... - else - { - # If this symbol is the interpretation of any references... - if ($symbolObject->HasReferences()) - { - # If this was the current interpretation of any references, reinterpret them and rebuild their files. - - my @references = $symbolObject->References(); - - foreach my $reference (@references) - { - if ($references{$reference}->CurrentInterpretation() eq $symbol) - { - $self->InterpretReference($reference); - $self->OnInterpretationChange($reference); - }; - }; - } - - # If there are no interpretations of the symbol... - else - { - # Delete the symbol entirely. - delete $symbols{$symbol}; - }; - }; - - # Remove it from the file index. - - $files{$file}->DeleteSymbol($symbol); - - if (!$files{$file}->HasAnything()) - { delete $files{$file}; }; - - - # We don't need to worry about the watched file, since this function will only be called by AnalyzeChanges() and - # LoadAndPurge(). - }; - }; - - -# -# Function: GenerateInterpretations -# -# Generates the list of interpretations for the passed reference. Also creates potential symbols as necessary. -# -# Parameters: -# -# referenceString - The <ReferenceString> to generate the interpretations of. -# -sub GenerateInterpretations #(referenceString) - { - my ($self, $referenceString) = @_; - - my ($type, $symbol, $scope, $using, $resolvingFlags) = NaturalDocs::ReferenceString->InformationOf($referenceString); - - # RESOLVE_NOPLURAL is handled by having @singulars be empty. - my @singulars; - if (!($resolvingFlags & ::RESOLVE_NOPLURAL())) - { @singulars = $self->SingularInterpretationsOf($symbol); }; - - # Since higher scores are better, we'll start at a high number and decrement. - my $score = 50000; - - - # If RESOLVE_RELATIVE is set, we do all the scope relatives before the global. - if ($resolvingFlags & ::RESOLVE_RELATIVE()) - { - $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $scope, $score); - } - - # If neither RESOLVE_RELATIVE nor RESOLVE_ABSOLUTE is set, we only do the local before the global. - elsif (!($resolvingFlags & ::RESOLVE_ABSOLUTE())) - { - $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($scope, $symbol), $score); - $score--; - - foreach my $singular (@singulars) - { - $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($scope, $singular), $score); - $score--; - }; - }; - - - # Do the global. - - $self->AddInterpretation($referenceString, $symbol, $score); - $score--; - - foreach my $singular (@singulars) - { - $self->AddInterpretation($referenceString, $singular, $score); - $score--; - }; - - - # If neither RESOLVE_RELATIVE nor RESOLVE_ABSOLUTE is set, we need to do the rest of the scope relatives after the global. - if (!($resolvingFlags & ::RESOLVE_RELATIVE()) && !($resolvingFlags & ::RESOLVE_ABSOLUTE())) - { - $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $scope, $score, 1); - }; - - - # Finally, if RESOLVE_NOUSING isn't set, go through the using scopes. - if (!($resolvingFlags & ::RESOLVE_NOUSING()) && defined $using) - { - foreach my $usingScope (@$using) - { - if ($resolvingFlags & ::RESOLVE_ABSOLUTE()) - { - $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($usingScope, $symbol), $score); - $score--; - - foreach my $singular (@singulars) - { - $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join($usingScope, $singular), $score); - $score--; - }; - } - else - { - $score = $self->GenerateRelativeInterpretations($referenceString, $symbol, \@singulars, $usingScope, $score); - }; - }; - }; - }; - - -# -# Function: GenerateRelativeInterpretations -# -# Generates the list of relative interpretations for the passed reference and packages. Also creates potential symbols as -# necessary. -# -# This function will _not_ create global interpretations. It _will_ create a local interpretations (symbol + all packages) unless -# you set dontUseFull. -# -# Parameters: -# -# referenceString - The <ReferenceString> to generate interpretations for. -# symbol - The <SymbolString> to generate interpretations of. -# singulars - A reference to an array of singular <SymbolStrings> to also generate interpretations of. Set to an empty array -# if none. -# package - The package <SymbolString> to use. May be undef. -# score - The starting score to apply. -# dontUseFull - Whether to not generate an interpretation including the full package identifier. If set, generated interpretations -# will start one level down. -# -# Returns: -# -# The next unused score. This is basically the passed score minus the number of interpretations created. -# -sub GenerateRelativeInterpretations #(referenceString, symbol, singulars, package, score, dontUseFull) - { - my ($self, $referenceString, $symbol, $singulars, $package, $score, $dontUseFull) = @_; - - my @packages = NaturalDocs::SymbolString->IdentifiersOf($package); - - # The last package index to include. This number is INCLUSIVE! - my $packageLevel = scalar @packages - 1; - - if ($dontUseFull) - { $packageLevel--; }; - - while ($packageLevel >= 0) - { - $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join(@packages[0..$packageLevel], $symbol), - $score); - $score--; - - foreach my $singular (@$singulars) - { - $self->AddInterpretation($referenceString, NaturalDocs::SymbolString->Join(@packages[0..$packageLevel], $singular), - $score); - $score--; - }; - - $packageLevel--; - }; - - return $score; - }; - - -# -# Function: SingularInterpretationsOf -# -# Generates singular interpretations of a <SymbolString> if it can be interpreted as a plural. Not all of them will be valid singular -# forms, but that doesn't matter since it's incredibly unlikely an invalid form would exist as a symbol. What matters is that the -# legimate singular is present on the list. -# -# Parameters: -# -# symbol - The <SymbolString>. -# -# Returns: -# -# An array of potential singular interpretations as <SymbolStrings>, in no particular order. If the symbol can't be interpreted -# as a plural, returns an empty array. -# -sub SingularInterpretationsOf #(symbol) - { - my ($self, $symbol) = @_; - - my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol); - my $lastIdentifier = pop @identifiers; - my $preIdentifiers = NaturalDocs::SymbolString->Join(@identifiers); - - my @results; - - # First cut off any 's or ' at the end, since they can appear after other plural forms. - if ($lastIdentifier =~ s/\'s?$//i) - { - push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $lastIdentifier); - }; - - # See http://www.gsu.edu/~wwwesl/egw/crump.htm for a good list of potential plural forms. There are a couple more than - # listed below, but they're fairly rare and this is already seriously over-engineered. This is split by suffix length to make - # comparisons more efficient. - - # The fact that this will generate some impossible combinations (leaves => leave, leav, leaf, leafe) doesn't matter. It's very - # unlikely that more than one will manage to match a defined symbol. Even if they do (leave, leaf), it's incredibly unlikely - # that someone has defined an impossible one (leav, leafe). So it's not so important that we remove impossible combinations, - # just that we include all the possible ones. - - my @suffixGroups = ( [ 's', undef, # boys => boy - 'i', 'us', # alumni => alumnus - 'a', 'um', # errata => erratum - 'a', 'on' ], # phenomena => phenomenon - - [ 'es', undef, # foxes => fox - 'ae', 'a' ], # amoebae => amoeba - - [ 'ies', 'y', # pennies => penny - 'ves', 'f', # calves => calf - 'ves', 'fe', # knives => knife - 'men', 'man', # women => woman - 'ice', 'ouse', # mice => mouse - 'oes', 'o', # vetoes => veto - 'ces', 'x', # matrices => matrix - 'xen', 'x' ], # oxen => ox - - [ 'ices', 'ex', # indices => index - 'feet', 'foot', # feet => foot - 'eese', 'oose', # geese => goose - 'eeth', 'ooth', # teeth => tooth - 'dren', 'd' ] ); # children => child - - my $suffixLength = 1; - - foreach my $suffixGroup (@suffixGroups) - { - my $identifierSuffix = lc( substr($lastIdentifier, 0 - $suffixLength) ); - my $cutIdentifier = substr($lastIdentifier, 0, 0 - $suffixLength); - - for (my $i = 0; $i + 1 < scalar @$suffixGroup; $i += 2) - { - my $suffix = $suffixGroup->[$i]; - my $replacement = $suffixGroup->[$i + 1]; - - if ($identifierSuffix eq $suffix) - { - if (defined $replacement) - { - push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier . $replacement); - push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier . uc($replacement)); - } - else - { - push @results, NaturalDocs::SymbolString->Join($preIdentifiers, $cutIdentifier); - }; - }; - }; - - $suffixLength++; - }; - - return @results; - }; - - -# -# Function: AddInterpretation -# -# Adds an interpretation to an existing reference. Creates potential symbols as necessary. -# -# Parameters: -# -# referenceString - The <ReferenceString> to add the interpretation to. -# symbol - The <SymbolString> the reference can be interpreted as. -# score - The score of the interpretation. -# -sub AddInterpretation #(referenceString, symbol, score) - { - my ($self, $referenceString, $symbol, $score) = @_; - - $references{$referenceString}->AddInterpretation($symbol, $score); - - # Create a potential symbol if it doesn't exist. - - if (!exists $symbols{$symbol}) - { $symbols{$symbol} = NaturalDocs::SymbolTable::Symbol->New(); }; - - $symbols{$symbol}->AddReference($referenceString, $score); - }; - - -# -# Function: InterpretReference -# -# Interprets the passed reference, matching it to the defined symbol with the highest score. If the symbol is already -# interpreted, it will reinterpret it. If there are no matches, it will make it an undefined reference. -# -# Parameters: -# -# referenceString - The <ReferenceString> to interpret. -# -sub InterpretReference #(referenceString) - { - my ($self, $referenceString) = @_; - - my $interpretation; - my $currentInterpretation; - my $score; - my $currentScore = -1; - - my $referenceObject = $references{$referenceString}; - - my %interpretationsAndScores = $referenceObject->InterpretationsAndScores(); - while ( ($interpretation, $score) = each %interpretationsAndScores ) - { - if ($score > $currentScore && $symbols{$interpretation}->IsDefined()) - { - $currentScore = $score; - $currentInterpretation = $interpretation; - }; - }; - - if ($currentScore > -1) - { $referenceObject->SetCurrentInterpretation($currentInterpretation); } - else - { $referenceObject->SetCurrentInterpretation(undef); }; - }; - - -# -# Function: MakeIndex -# -# Generates a symbol index. -# -# Parameters: -# -# type - The <TopicType> to limit the index to. -# -# Returns: -# -# An arrayref of sections. The first represents all the symbols, the second the numbers, and the rest A through Z. -# Each section is a sorted arrayref of <NaturalDocs::SymbolTable::IndexElement> objects. If a section has no content, -# it will be undef. -# -sub MakeIndex #(type) - { - my ($self, $type) = @_; - - - # Go through the symbols and generate IndexElements for any that belong in the index. - - # Keys are the symbol strings, values are IndexElements. - my %indexSymbols; - - while (my ($symbolString, $object) = each %symbols) - { - my ($symbol, $package) = $self->SplitSymbolForIndex($symbolString, $object->GlobalType()); - my @definitions = $object->Definitions(); - - foreach my $definition (@definitions) - { - my $definitionType = $object->TypeDefinedIn($definition); - - if ($type eq ::TOPIC_GENERAL() || $type eq $definitionType) - { - if (!exists $indexSymbols{$symbol}) - { - $indexSymbols{$symbol} = - NaturalDocs::SymbolTable::IndexElement->New($symbol, $package, $definition, $definitionType, - $object->PrototypeDefinedIn($definition), - $object->SummaryDefinedIn($definition) ); - } - else - { - $indexSymbols{$symbol}->Merge($package, $definition, $definitionType, - $object->PrototypeDefinedIn($definition), - $object->SummaryDefinedIn($definition) ); - }; - }; # If type matches - }; # Each definition - }; # Each symbol - - - # Generate sortable symbols for each IndexElement, sort them internally, and divide them into sections. - - my $sections = [ ]; - - foreach my $indexElement (values %indexSymbols) - { - $indexElement->Sort(); - $indexElement->MakeSortableSymbol(); - - my $sectionNumber; - - if ($indexElement->SortableSymbol() =~ /^([a-z])/i) - { $sectionNumber = ord(lc($1)) - ord('a') + 2; } - elsif ($indexElement->SortableSymbol() =~ /^[0-9]/) - { $sectionNumber = 1; } - else - { $sectionNumber = 0; }; - - if (!defined $sections->[$sectionNumber]) - { $sections->[$sectionNumber] = [ ]; }; - - push @{$sections->[$sectionNumber]}, $indexElement; - }; - - - # Sort each section. - - for (my $i = 0; $i < scalar @$sections; $i++) - { - if (defined $sections->[$i]) - { - @{$sections->[$i]} = sort - { - my $result = ::StringCompare($a->SortableSymbol(), $b->SortableSymbol()); - - if ($result == 0) - { $result = ::StringCompare($a->IgnoredPrefix(), $b->IgnoredPrefix()); }; - - return $result; - } - @{$sections->[$i]}; - }; - }; - - - return $sections; - }; - - -# -# Function: SplitSymbolForIndex -# -# Splits a <SymbolString> into its symbol and package portions for indexing. -# -# Parameters: -# -# symbol - The <SymbolString>. -# type - Its <TopicType>. -# -# Returns: -# -# The array ( symbol, package ), which are both <SymbolStrings>. If the symbol is global, package will be undef. -# -sub SplitSymbolForIndex #(symbol, type) - { - my ($self, $symbol, $type) = @_; - - my $scope = NaturalDocs::Topics->TypeInfo($type)->Scope(); - - if ($scope == ::SCOPE_START() || $scope == ::SCOPE_ALWAYS_GLOBAL()) - { return ( $symbol, undef ); } - else - { - my @identifiers = NaturalDocs::SymbolString->IdentifiersOf($symbol); - - $symbol = pop @identifiers; - my $package = NaturalDocs::SymbolString->Join(@identifiers); - - return ( $symbol, $package ); - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/SymbolTable/File.pm b/docs/doctool/Modules/NaturalDocs/SymbolTable/File.pm deleted file mode 100644 index 712a9322..00000000 --- a/docs/doctool/Modules/NaturalDocs/SymbolTable/File.pm +++ /dev/null @@ -1,186 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::SymbolTable::File -# -############################################################################### -# -# A class representing a file, keeping track of what symbols and references are defined in it. -# -############################################################################### - -# 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::SymbolTable::File; - - -############################################################################### -# Group: Implementation - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The following constants are its members. -# -# SYMBOLS - An existence hashref of the <SymbolStrings> it defines. -# REFERENCES - An existence hashref of the <ReferenceStrings> in the file. -# - -# DEPENDENCY: New() depends on the order of these constants. If they change, New() has to be updated. -use constant SYMBOLS => 0; -use constant REFERENCES => 1; - - -############################################################################### -# Group: Modification Functions - - -# -# Function: New -# -# Creates and returns a new object. -# -sub New - { - my $package = shift; - - # Let's make it safe, since normally you can pass values to New. Having them just be ignored would be an obscure error. - if (scalar @_) - { die "You can't pass values to NaturalDocs::SymbolTable::File->New()\n"; }; - - # DEPENDENCY: This code depends on the order of the member constants. - my $object = [ { }, { } ]; - bless $object, $package; - - return $object; - }; - - -# -# Function: AddSymbol -# -# Adds a <SymbolString> definition. -# -# Parameters: -# -# symbol - The <SymbolString> being added. -# -sub AddSymbol #(symbol) - { - my ($self, $symbol) = @_; - $self->[SYMBOLS]{$symbol} = 1; - }; - - -# -# Function: DeleteSymbol -# -# Removes a <SymbolString> definition. -# -# Parameters: -# -# symbol - The <SymbolString> to delete. -# -sub DeleteSymbol #(symbol) - { - my ($self, $symbol) = @_; - delete $self->[SYMBOLS]{$symbol}; - }; - - -# -# Function: AddReference -# -# Adds a reference definition. -# -# Parameters: -# -# referenceString - The <ReferenceString> being added. -# -sub AddReference #(referenceString) - { - my ($self, $referenceString) = @_; - $self->[REFERENCES]{$referenceString} = 1; - }; - - -# -# Function: DeleteReference -# -# Removes a reference definition. -# -# Parameters: -# -# referenceString - The <ReferenceString> to delete. -# -sub DeleteReference #(referenceString) - { - my ($self, $referenceString) = @_; - delete $self->[REFERENCES]{$referenceString}; - }; - - - -############################################################################### -# Group: Information Functions - - -# -# Function: HasAnything -# -# Returns whether the file has any symbol or reference definitions at all. -# -sub HasAnything - { - return (scalar keys %{$_[0]->[SYMBOLS]} || scalar keys %{$_[0]->[REFERENCES]}); - }; - -# -# Function: Symbols -# -# Returns an array of all the <SymbolStrings> defined in this file. If none, returns an empty array. -# -sub Symbols - { - return keys %{$_[0]->[SYMBOLS]}; - }; - - -# -# Function: References -# -# Returns an array of all the <ReferenceStrings> defined in this file. If none, returns an empty array. -# -sub References - { - return keys %{$_[0]->[REFERENCES]}; - }; - - -# -# Function: DefinesSymbol -# -# Returns whether the file defines the passed <SymbolString> or not. -# -sub DefinesSymbol #(symbol) - { - my ($self, $symbol) = @_; - return exists $self->[SYMBOLS]{$symbol}; - }; - - -# -# Function: DefinesReference -# -# Returns whether the file defines the passed <ReferenceString> or not. -# -sub DefinesReference #(referenceString) - { - my ($self, $referenceString) = @_; - return exists $self->[REFERENCES]{$referenceString}; - }; - -1; diff --git a/docs/doctool/Modules/NaturalDocs/SymbolTable/IndexElement.pm b/docs/doctool/Modules/NaturalDocs/SymbolTable/IndexElement.pm deleted file mode 100644 index da08cf4f..00000000 --- a/docs/doctool/Modules/NaturalDocs/SymbolTable/IndexElement.pm +++ /dev/null @@ -1,522 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::SymbolTable::IndexElement -# -############################################################################### -# -# A class representing part of an indexed symbol. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - -use Tie::RefHash; - -use strict; -use integer; - - -package NaturalDocs::SymbolTable::IndexElement; - - -# -# Topic: How IndexElements Work -# -# This is a little tricky, so make sure you understand this. Indexes are sorted by symbol, then packages, then file. If there is only -# one package for a symbol, or one file definition for a package/symbol, they are added inline to the entry. However, if there are -# multiple packages or files, the function for it returns an arrayref of IndexElements instead. Which members are defined and -# undefined should follow common sense. For example, if a symbol is defined in multiple packages, the symbol's IndexElement -# will not define <File()>, <Type()>, or <Prototype()>; those will be defined in child elements. Similarly, the child elements will -# not define <Symbol()> since it's redundant. -# -# Diagrams may be clearer. If a member isn't listed for an element, it isn't defined. -# -# A symbol that only has one package and file: -# > [Element] -# > - Symbol -# > - Package -# > - File -# > - Type -# > - Prototype -# > - Summary -# -# A symbol that is defined by multiple packages, each with only one file: -# > [Element] -# > - Symbol -# > - Package -# > [Element] -# > - Package -# > - File -# > - Type -# > - Prototype -# > - Summary -# > [Element] -# > - ... -# -# A symbol that is defined by one package, but has multiple files -# > [Element] -# > - Symbol -# > - Package -# > - File -# > [Element] -# > - File -# > - Type -# > - Protype -# > - Summary -# > [Element] -# > - ... -# -# A symbol that is defined by multiple packages which have multiple files: -# > [Element] -# > - Symbol -# > - Package -# > [Element] -# > - Package -# > - File -# > [Element] -# > - File -# > - Type -# > - Prototype -# > - Summary -# > [Element] -# > - ... -# > [Element] -# > - ... -# -# Why is it done this way?: -# -# Because it makes it easier to generate nice indexes since all the splitting and combining is done for you. If a symbol -# has only one package, you just want to link to it, you don't want to break out a subindex for just one package. However, if -# it has multiple package, you do want the subindex and to link to each one individually. Use <HasMultiplePackages()> and -# <HasMultipleFiles()> to determine whether you need to add a subindex for it. -# -# -# Combining Properties: -# -# All IndexElements also have combining properties set. -# -# CombinedType - The general <TopicType> of the entry. Conflicts combine into <TOPIC_GENERAL>. -# PackageSeparator - The package separator symbol of the entry. Conflicts combine into a dot. -# -# So if an IndexElement only has one definition, <CombinedType()> is the same as the <TopicType> and <PackageSeparator()> -# is that of the definition's language. If other definitions are added and they have the same properties, the combined properties -# will remain the same. However, if they're different, they switch values as noted above. -# -# -# Sortable Symbol: -# -# <SortableSymbol()> is a pseudo-combining property. There were a few options for dealing with multiple languages defining -# the same symbol but stripping different prefixes off it, but ultimately I decided to go with whatever the language does that -# has the most definitions. There's not likely to be many conflicts here in the real world; probably the only thing would be -# defining it in a text file and forgetting to specify the prefixes to strip there too. So this works. -# -# Ties are broken pretty much randomly, except that text files always lose if its one of the options. -# -# It's a pseudo-combining property because it's done after the IndexElements are all filled in and only stored in the top-level -# ones. -# - - -############################################################################### -# Group: Implementation - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The following constants are its members. -# -# SYMBOL - The <SymbolString> without the package portion. -# PACKAGE - The package <SymbolString>. Will be a package <SymbolString>, undef for global, or an arrayref of -# <NaturalDocs::SymbolTable::IndexElement> objects if multiple packages define the symbol. -# FILE - The <FileName> the package/symbol is defined in. Will be the file name or an arrayref of -# <NaturalDocs::SymbolTable::IndexElements> if multiple files define the package/symbol. -# TYPE - The package/symbol/file <TopicType>. -# PROTOTYPE - The package/symbol/file prototype, or undef if not applicable. -# SUMMARY - The package/symbol/file summary, or undef if not applicable. -# COMBINED_TYPE - The combined <TopicType> of the element. -# PACKAGE_SEPARATOR - The combined package separator symbol of the element. -# SORTABLE_SYMBOL - The sortable symbol as a text string. -# IGNORED_PREFIX - The part of the symbol that was stripped off to make the sortable symbol. -# -use NaturalDocs::DefineMembers 'SYMBOL', 'Symbol()', - 'PACKAGE', 'Package()', - 'FILE', 'File()', - 'TYPE', 'Type()', - 'PROTOTYPE', 'Prototype()', - 'SUMMARY', 'Summary()', - 'COMBINED_TYPE', 'CombinedType()', - 'PACKAGE_SEPARATOR', 'PackageSeparator()', - 'SORTABLE_SYMBOL', 'SortableSymbol()', - 'IGNORED_PREFIX', 'IgnoredPrefix()'; -# DEPENDENCY: New() depends on the order of these constants and that there is no inheritance.. - - -############################################################################### -# Group: Modification Functions - -# -# Function: New -# -# Returns a new object. -# -# This should only be used for creating an entirely new symbol. You should *not* pass arrayrefs as package or file parameters -# if you are calling this externally. Use <Merge()> instead. -# -# Parameters: -# -# symbol - The <SymbolString> without the package portion. -# package - The package <SymbolString>, or undef for global. -# file - The symbol's definition file. -# type - The symbol's <TopicType>. -# prototype - The symbol's prototype, if applicable. -# summary - The symbol's summary, if applicable. -# -# Optional Parameters: -# -# These parameters don't need to be specified. You should ignore them when calling this externally. -# -# combinedType - The symbol's combined <TopicType>. -# packageSeparator - The symbol's combined package separator symbol. -# -sub New #(symbol, package, file, type, prototype, summary, combinedType, packageSeparator) - { - # DEPENDENCY: This depends on the parameter list being in the same order as the constants. - - my $self = shift; - - my $object = [ @_ ]; - bless $object, $self; - - if (!defined $object->[COMBINED_TYPE]) - { $object->[COMBINED_TYPE] = $object->[TYPE]; }; - - if (!defined $object->[PACKAGE_SEPARATOR]) - { - if ($object->[TYPE] eq ::TOPIC_FILE()) - { $object->[PACKAGE_SEPARATOR] = '.'; } - else - { - $object->[PACKAGE_SEPARATOR] = NaturalDocs::Languages->LanguageOf($object->[FILE])->PackageSeparator(); - }; - }; - - return $object; - }; - - -# -# Function: Merge -# -# Adds another definition of the same symbol. Perhaps it has a different package or defining file. -# -# Parameters: -# -# package - The package <SymbolString>, or undef for global. -# file - The symbol's definition file. -# type - The symbol's <TopicType>. -# prototype - The symbol's protoype if applicable. -# summary - The symbol's summary if applicable. -# -sub Merge #(package, file, type, prototype, summary) - { - my ($self, $package, $file, $type, $prototype, $summary) = @_; - - # If there's only one package... - if (!$self->HasMultiplePackages()) - { - # If there's one package and it's the same as the new one... - if ($package eq $self->Package()) - { - $self->MergeFile($file, $type, $prototype, $summary); - } - - # If there's one package and the new one is different... - else - { - my $selfDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, $self->Package(), $self->File(), - $self->Type(), $self->Prototype(), - $self->Summary(), $self->CombinedType(), - $self->PackageSeparator()); - my $newDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, $package, $file, $type, $prototype, - $summary); - - $self->[PACKAGE] = [ $selfDefinition, $newDefinition ]; - $self->[FILE] = undef; - $self->[TYPE] = undef; - $self->[PROTOTYPE] = undef; - $self->[SUMMARY] = undef; - - if ($newDefinition->Type() ne $self->CombinedType()) - { $self->[COMBINED_TYPE] = ::TOPIC_GENERAL(); }; - if ($newDefinition->PackageSeparator() ne $self->PackageSeparator()) - { $self->[PACKAGE_SEPARATOR] = '.'; }; - }; - } - - # If there's more than one package... - else - { - # See if the new package is one of them. - my $selfPackages = $self->Package(); - my $matchingPackage; - - foreach my $testPackage (@$selfPackages) - { - if ($package eq $testPackage->Package()) - { - $testPackage->MergeFile($file, $type, $prototype, $summary);; - return; - }; - }; - - my $newDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, $package, $file, $type, $prototype, - $summary); - push @{$self->[PACKAGE]}, $newDefinition; - - if ($newDefinition->Type() ne $self->CombinedType()) - { $self->[COMBINED_TYPE] = ::TOPIC_GENERAL(); }; - if ($newDefinition->PackageSeparator() ne $self->PackageSeparator()) - { $self->[PACKAGE_SEPARATOR] = '.'; }; - }; - }; - - -# -# Function: Sort -# -# Sorts the package and file lists of the symbol. -# -sub Sort - { - my $self = shift; - - if ($self->HasMultipleFiles()) - { - @{$self->[FILE]} = sort { ::StringCompare($a->File(), $b->File()) } @{$self->File()}; - } - - elsif ($self->HasMultiplePackages()) - { - @{$self->[PACKAGE]} = sort { ::StringCompare( $a->Package(), $b->Package()) } @{$self->[PACKAGE]}; - - foreach my $packageElement ( @{$self->[PACKAGE]} ) - { - if ($packageElement->HasMultipleFiles()) - { $packageElement->Sort(); }; - }; - }; - }; - - -# -# Function: MakeSortableSymbol -# -# Generates <SortableSymbol()> and <IgnoredPrefix()>. Should only be called after everything is merged. -# -sub MakeSortableSymbol - { - my $self = shift; - - my $finalLanguage; - - if ($self->HasMultiplePackages() || $self->HasMultipleFiles()) - { - # Collect all the files that define this symbol. - - my @files; - - if ($self->HasMultipleFiles()) - { - my $fileElements = $self->File(); - - foreach my $fileElement (@$fileElements) - { push @files, $fileElement->File(); }; - } - else # HasMultiplePackages - { - my $packages = $self->Package(); - - foreach my $package (@$packages) - { - if ($package->HasMultipleFiles()) - { - my $fileElements = $package->File(); - - foreach my $fileElement (@$fileElements) - { push @files, $fileElement->File(); }; - } - else - { push @files, $package->File(); }; - }; - }; - - - # Determine which language defines it the most. - - # Keys are language objects, values are counts. - my %languages; - tie %languages, 'Tie::RefHash'; - - foreach my $file (@files) - { - my $language = NaturalDocs::Languages->LanguageOf($file); - - if (exists $languages{$language}) - { $languages{$language}++; } - else - { $languages{$language} = 1; }; - }; - - my $topCount = 0; - my @topLanguages; - - while (my ($language, $count) = each %languages) - { - if ($count > $topCount) - { - $topCount = $count; - @topLanguages = ( $language ); - } - elsif ($count == $topCount) - { - push @topLanguages, $language; - }; - }; - - if (scalar @topLanguages == 1) - { $finalLanguage = $topLanguages[0]; } - else - { - if ($topLanguages[0]->Name() ne 'Text File') - { $finalLanguage = $topLanguages[0]; } - else - { $finalLanguage = $topLanguages[1]; }; - }; - } - - else # !hasMultiplePackages && !hasMultipleFiles - { $finalLanguage = NaturalDocs::Languages->LanguageOf($self->File()); }; - - my $textSymbol = NaturalDocs::SymbolString->ToText($self->Symbol(), $self->PackageSeparator()); - my $ignoredPrefixLength = $finalLanguage->IgnoredPrefixLength($textSymbol, $self->CombinedType()); - - if ($ignoredPrefixLength) - { - $self->[IGNORED_PREFIX] = substr($textSymbol, 0, $ignoredPrefixLength); - $self->[SORTABLE_SYMBOL] = substr($textSymbol, $ignoredPrefixLength); - } - else - { $self->[SORTABLE_SYMBOL] = $textSymbol; }; - }; - - - -############################################################################### -# -# Functions: Information Functions -# -# Symbol - Returns the <SymbolString> without the package portion. -# Package - If <HasMultiplePackages()> is true, returns an arrayref of <NaturalDocs::SymbolTable::IndexElement> objects. -# Otherwise returns the package <SymbolString>, or undef if global. -# File - If <HasMultipleFiles()> is true, returns an arrayref of <NaturalDocs::SymbolTable::IndexElement> objects. Otherwise -# returns the name of the definition file. -# Type - Returns the <TopicType> of the package/symbol/file, if applicable. -# Prototype - Returns the prototype of the package/symbol/file, if applicable. -# Summary - Returns the summary of the package/symbol/file, if applicable. -# CombinedType - Returns the combined <TopicType> of the element. -# PackageSeparator - Returns the combined package separator symbol of the element. -# SortableSymbol - Returns the sortable symbol as a text string. Only available after calling <MakeSortableSymbol()>. -# IgnoredPrefix - Returns the part of the symbol that was stripped off to make the <SortableSymbol()>, or undef if none. -# Only available after calling <MakeSortableSymbol()>. -# - -# Function: HasMultiplePackages -# Returns whether <Packages()> is broken out into more elements. -sub HasMultiplePackages - { return ref($_[0]->[PACKAGE]); }; - -# Function: HasMultipleFiles -# Returns whether <File()> is broken out into more elements. -sub HasMultipleFiles - { return ref($_[0]->[FILE]); }; - - - - - - -############################################################################### -# Group: Support Functions - -# -# Function: MergeFile -# -# Adds another definition of the same package/symbol. Perhaps the file is different. -# -# Parameters: -# -# file - The package/symbol's definition file. -# type - The package/symbol's <TopicType>. -# prototype - The package/symbol's protoype if applicable. -# summary - The package/symbol's summary if applicable. -# -sub MergeFile #(file, type, prototype, summary) - { - my ($self, $file, $type, $prototype, $summary) = @_; - - # If there's only one file... - if (!$self->HasMultipleFiles()) - { - # If there's one file and it's the different from the new one... - if ($file ne $self->File()) - { - my $selfDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, undef, $self->File(), $self->Type(), - $self->Prototype(), $self->Summary(), - $self->CombinedType(), - $self->PackageSeparator()); - my $newDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, undef, $file, $type, $prototype, - $summary); - - $self->[FILE] = [ $selfDefinition, $newDefinition ]; - $self->[TYPE] = undef; - $self->[PROTOTYPE] = undef; - $self->[SUMMARY] = undef; - - if ($newDefinition->Type() ne $self->CombinedType()) - { $self->[COMBINED_TYPE] = ::TOPIC_GENERAL(); }; - if ($newDefinition->PackageSeparator() ne $self->PackageSeparator()) - { $self->[PACKAGE_SEPARATOR] = '.'; }; - } - - # If the file was the same, just ignore the duplicate in the index. - } - - # If there's more than one file... - else - { - # See if the new file is one of them. - my $files = $self->File(); - - foreach my $testElement (@$files) - { - if ($testElement->File() eq $file) - { - # If the new file's already in the index, ignore the duplicate. - return; - }; - }; - - my $newDefinition = NaturalDocs::SymbolTable::IndexElement->New(undef, undef, $file, $type, $prototype, - $summary); - push @{$self->[FILE]}, $newDefinition; - - if ($newDefinition->Type() ne $self->CombinedType()) - { $self->[COMBINED_TYPE] = ::TOPIC_GENERAL(); }; - if ($newDefinition->PackageSeparator() ne $self->PackageSeparator()) - { $self->[PACKAGE_SEPARATOR] = '.'; }; - }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/SymbolTable/Reference.pm b/docs/doctool/Modules/NaturalDocs/SymbolTable/Reference.pm deleted file mode 100644 index 830bf60b..00000000 --- a/docs/doctool/Modules/NaturalDocs/SymbolTable/Reference.pm +++ /dev/null @@ -1,273 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::SymbolTable::Reference -# -############################################################################### -# -# A class representing a symbol or a potential symbol. -# -############################################################################### - -# 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::SymbolTable::Reference; - - -############################################################################### -# Group: Implementation - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The following constants are its members. -# -# DEFINITIONS - An existence hashref of the <FileNames> that define this reference. -# INTERPRETATIONS - A hashref of the possible interpretations of this reference. The keys are the <SymbolStrings> -# and the values are the scores. -# CURRENT_INTERPRETATION - The interpretation currently used as the reference target. It will be the interpretation with -# the highest score that is actually defined. If none are defined, this item will be undef. -# - -# DEPENDENCY: New() depends on the order of these constants. If they change, New() has to be updated. -use constant DEFINITIONS => 0; -use constant INTERPRETATIONS => 1; -use constant CURRENT_INTERPRETATION => 2; - - -############################################################################### -# Group: Modification Functions - - -# -# Function: New -# -# Creates and returns a new object. -# -sub New - { - my $package = shift; - - # Let's make it safe, since normally you can pass values to New. Having them just be ignored would be an obscure error. - if (scalar @_) - { die "You can't pass values to NaturalDocs::SymbolTable::Reference->New()\n"; }; - - # DEPENDENCY: This code depends on the order of the member constants. - my $object = [ { }, { }, undef ]; - bless $object, $package; - - return $object; - }; - - -# -# Function: AddDefinition -# -# Adds a reference definition. -# -# Parameters: -# -# file - The <FileName> that defines the reference. -# -sub AddDefinition #(file) - { - my ($self, $file) = @_; - - $self->[DEFINITIONS]{$file} = 1; - }; - - -# -# Function: DeleteDefinition -# -# Removes a reference definition. -# -# Parameters: -# -# file - The <FileName> which has the definition to delete. -# -sub DeleteDefinition #(file) - { - my ($self, $file) = @_; - - delete $self->[DEFINITIONS]{$file}; - }; - - -# -# Function: AddInterpretation -# -# Adds a symbol that this reference can be interpreted as. -# -# Parameters: -# -# symbol - The <SymbolString>. -# score - The score of this interpretation. -# -sub AddInterpretation #(symbol, score) - { - my ($self, $symbol, $score) = @_; - - $self->[INTERPRETATIONS]{$symbol} = $score; - }; - - -# -# Function: DeleteInterpretation -# -# Deletes a symbol that this reference can be interpreted as. -# -# Parameters: -# -# symbol - The <SymbolString> to delete. -# -sub DeleteInterpretation #(symbol) - { - my ($self, $symbol) = @_; - - delete $self->[INTERPRETATIONS]{$symbol}; - }; - - -# -# Function: DeleteAllInterpretationsButCurrent -# -# Deletes all interpretations except for the current one. -# -sub DeleteAllInterpretationsButCurrent - { - my $self = shift; - - if ($self->HasCurrentInterpretation()) - { - my $score = $self->CurrentScore(); - - # Fastest way to clear a hash except for one item? Make a new hash with just that item. - %{$self->[INTERPRETATIONS]} = ( $self->[CURRENT_INTERPRETATION] => $score ); - }; - }; - - -# -# Function: SetCurrentInterpretation -# -# Changes the current interpretation. The new one must already have been added via <AddInterpretation()>. -# -# Parameters: -# -# symbol - The <SymbolString>l to make the current interpretation. Can be set to undef to clear it. -# -sub SetCurrentInterpretation #(symbol) - { - my ($self, $symbol) = @_; - - $self->[CURRENT_INTERPRETATION] = $symbol; - }; - - -############################################################################### -# Group: Information Functions - - -# -# Function: Definitions -# -# Returns an array of all the <FileNames> that define this reference. If none do, returns an empty array. -# -sub Definitions - { - return keys %{$_[0]->[DEFINITIONS]}; - }; - - -# -# Function: IsDefined -# -# Returns whether the reference has any definitions or not. -# -sub IsDefined - { - return scalar keys %{$_[0]->[DEFINITIONS]}; - }; - - -# -# Function: IsDefinedIn -# -# Returns whether the reference is defined in the passed <FileName>. -# -sub IsDefinedIn #(file) - { - my ($self, $file) = @_; - - return exists $self->[DEFINITIONS]{$file}; - }; - - -# -# Function: Interpretations -# -# Returns an array of all the <SymbolStrings> that this reference can be interpreted as. If none, returns an empty array. -# -sub Interpretations - { - return keys %{$_[0]->[INTERPRETATIONS]}; - }; - - -# -# Function: InterpretationsAndScores -# -# Returns a hash of all the <SymbolStrings> that this reference can be interpreted as and their scores. The keys are the <SymbolStrings> -# and the values are the scores. If none, returns an empty hash. -# -sub InterpretationsAndScores - { - return %{$_[0]->[INTERPRETATIONS]}; - }; - - -# -# Function: HasCurrentInterpretation -# -# Returns whether the reference has a current interpretation or not. -# -sub HasCurrentInterpretation - { - return defined $_[0]->[CURRENT_INTERPRETATION]; - }; - - -# -# Function: CurrentInterpretation -# -# Returns the <SymbolString> of the current interpretation, or undef if none. -# -sub CurrentInterpretation - { - return $_[0]->[CURRENT_INTERPRETATION]; - }; - - -# -# Function: CurrentScore -# -# Returns the score of the current interpretation, or undef if none. -# -sub CurrentScore - { - my $self = shift; - - if (defined $self->[CURRENT_INTERPRETATION]) - { - return $self->[INTERPRETATIONS]{ $self->[CURRENT_INTERPRETATION] }; - } - else - { return undef; }; - }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm b/docs/doctool/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm deleted file mode 100644 index bff8fb23..00000000 --- a/docs/doctool/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm +++ /dev/null @@ -1,97 +0,0 @@ -############################################################################### -# -# Class: NaturalDocs::SymbolTable::ReferenceTarget -# -############################################################################### -# -# A class for storing information about a reference target. -# -############################################################################### - -# 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::SymbolTable::ReferenceTarget; - - -############################################################################### -# Group: Implementation - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The following constants are its members. -# -# SYMBOL - The target <SymbolString>. -# FILE - The <FileName> the target is defined in. -# TYPE - The target <TopicType>. -# PROTOTYPE - The target's prototype, or undef if none. -# SUMMARY - The target's summary, or undef if none. -# - -# DEPENDENCY: New() depends on the order of these constants. If they change, New() has to be updated. -use constant SYMBOL => 0; -use constant FILE => 1; -use constant TYPE => 2; -use constant PROTOTYPE => 3; -use constant SUMMARY => 4; - -############################################################################### -# Group: Functions - - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# symbol - The target <SymbolString>. -# file - The <FileName> the target is defined in. -# type - The <TopicType> of the target symbol. -# prototype - The target's prototype. Set to undef if not defined or not applicable. -# summary - The target's summary. Set to undef if not defined or not applicable. -# -sub New #(symbol, file, type, prototype, summary) - { - # DEPENDENCY: This code depends on the order of the member constants. - - my $package = shift; - - my $object = [ @_ ]; - bless $object, $package; - - return $object; - }; - - -# Function: Symbol -# Returns the target's <SymbolString>. -sub Symbol - { return $_[0]->[SYMBOL]; }; - -# Function: File -# Returns the <FileName> the target is defined in. -sub File - { return $_[0]->[FILE]; }; - -# Function: Type -# Returns the target's <TopicType>. -sub Type - { return $_[0]->[TYPE]; }; - -# Function: Prototype -# Returns the target's prototype, or undef if not defined or not applicable. -sub Prototype - { return $_[0]->[PROTOTYPE]; }; - -# Function: Summary -# Returns the target's summary, or undef if not defined or not applicable. -sub Summary - { return $_[0]->[SUMMARY]; }; - -1; diff --git a/docs/doctool/Modules/NaturalDocs/SymbolTable/Symbol.pm b/docs/doctool/Modules/NaturalDocs/SymbolTable/Symbol.pm deleted file mode 100644 index b607269a..00000000 --- a/docs/doctool/Modules/NaturalDocs/SymbolTable/Symbol.pm +++ /dev/null @@ -1,428 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::SymbolTable::Symbol -# -############################################################################### -# -# A class representing a symbol or a potential symbol. -# -############################################################################### - -# 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::SymbolTable::Symbol; - - -############################################################################### -# Group: Implementation - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The following constants are its members. -# -# DEFINITIONS - A hashref of all the files which define this symbol. The keys are the <FileNames>, and the values are -# <NaturalDocs::SymbolTable::SymbolDefinition> objects. If no files define this symbol, this item will -# be undef. -# GLOBAL_DEFINITION - The <FileName> which defines the global version of the symbol, which is what is used if -# a file references the symbol but does not have its own definition. If there are no definitions, this -# item will be undef. -# REFERENCES - A hashref of the references that can be interpreted as this symbol. This doesn't mean these -# references necessarily are. The keys are the reference strings, and the values are the scores of -# the interpretations. If no references can be interpreted as this symbol, this item will be undef. -# -use constant DEFINITIONS => 0; -use constant GLOBAL_DEFINITION => 1; -use constant REFERENCES => 2; - - -############################################################################### -# Group: Modification Functions - -# -# Function: New -# -# Creates and returns a new object. -# -sub New - { - my $package = shift; - - # Let's make it safe, since normally you can pass values to New. Having them just be ignored would be an obscure error. - if (scalar @_) - { die "You can't pass values to NaturalDocs::SymbolTable::Symbol->New()\n"; }; - - my $object = [ undef, undef, undef ]; - bless $object, $package; - - return $object; - }; - -# -# Function: AddDefinition -# -# Adds a symbol definition. If this is the first definition for this symbol, it will become the global definition. If the definition -# already exists for the file, it will be ignored. -# -# Parameters: -# -# file - The <FileName> that defines the symbol. -# type - The <TopicType> of the definition. -# prototype - The prototype of the definition, if applicable. Undef otherwise. -# summary - The summary for the definition, if applicable. Undef otherwise. -# -# Returns: -# -# Whether this provided the first definition for this symbol. -# -sub AddDefinition #(file, type, prototype, summary) - { - my ($self, $file, $type, $prototype, $summary) = @_; - - my $isFirst; - - if (!defined $self->[DEFINITIONS]) - { - $self->[DEFINITIONS] = { }; - $self->[GLOBAL_DEFINITION] = $file; - $isFirst = 1; - }; - - if (!exists $self->[DEFINITIONS]{$file}) - { - $self->[DEFINITIONS]{$file} = NaturalDocs::SymbolTable::SymbolDefinition->New($type, $prototype, $summary); - }; - - return $isFirst; - }; - - -# -# Function: ChangeDefinition -# -# Changes the information about an existing definition. -# -# Parameters: -# -# file - The <FileName> that defines the symbol. Must exist. -# type - The new <TopicType> of the definition. -# prototype - The new prototype of the definition, if applicable. Undef otherwise. -# summary - The new summary of the definition, if applicable. Undef otherwise. -# -sub ChangeDefinition #(file, type, prototype, summary) - { - my ($self, $file, $type, $prototype, $summary) = @_; - - if (defined $self->[DEFINITIONS] && - exists $self->[DEFINITIONS]{$file}) - { - $self->[DEFINITIONS]{$file}->SetType($type); - $self->[DEFINITIONS]{$file}->SetPrototype($prototype); - $self->[DEFINITIONS]{$file}->SetSummary($summary); - }; - }; - - -# -# Function: DeleteDefinition -# -# Removes a symbol definition. If the definition served as the global definition, a new one will be selected. -# -# Parameters: -# -# file - The <FileName> which contains definition to delete. -# -# Returns: -# -# Whether that was the only definition, and the symbol is now undefined. -# -sub DeleteDefinition #(file) - { - my ($self, $file) = @_; - - # If there are no definitions... - if (!defined $self->[DEFINITIONS]) - { return undef; }; - - delete $self->[DEFINITIONS]{$file}; - - # If there are no more definitions... - if (!scalar keys %{$self->[DEFINITIONS]}) - { - $self->[DEFINITIONS] = undef; - - # If definitions was previously defined, and now is empty, we can safely assume that the global definition was just deleted - # without checking it against $file. - - $self->[GLOBAL_DEFINITION] = undef; - - return 1; - } - - # If there are more definitions and the global one was just deleted... - elsif ($self->[GLOBAL_DEFINITION] eq $file) - { - # Which one becomes global is pretty much random. - $self->[GLOBAL_DEFINITION] = (keys %{$self->[DEFINITIONS]})[0]; - return undef; - }; - }; - - -# -# Function: AddReference -# -# Adds a reference that can be interpreted as this symbol. It can be, but not necessarily is. -# -# Parameters: -# -# referenceString - The string of the reference. -# score - The score of this interpretation. -# -sub AddReference #(referenceString, score) - { - my ($self, $referenceString, $score) = @_; - - if (!defined $self->[REFERENCES]) - { $self->[REFERENCES] = { }; }; - - $self->[REFERENCES]{$referenceString} = $score; - }; - - -# -# Function: DeleteReference -# -# Deletes a reference that can be interpreted as this symbol. -# -# Parameters: -# -# referenceString - The string of the reference to delete. -# -sub DeleteReference #(referenceString) - { - my ($self, $referenceString) = @_; - - # If there are no definitions... - if (!defined $self->[REFERENCES]) - { return; }; - - delete $self->[REFERENCES]{$referenceString}; - - # If there are no more definitions... - if (!scalar keys %{$self->[REFERENCES]}) - { - $self->[REFERENCES] = undef; - }; - }; - - -# -# Function: DeleteAllReferences -# -# Removes all references that can be interpreted as this symbol. -# -sub DeleteAllReferences - { - $_[0]->[REFERENCES] = undef; - }; - - -############################################################################### -# Group: Information Functions - -# -# Function: IsDefined -# -# Returns whether the symbol is defined anywhere or not. If it's not, that means it's just a potential interpretation of a -# reference. -# -sub IsDefined - { - return defined $_[0]->[GLOBAL_DEFINITION]; - }; - -# -# Function: IsDefinedIn -# -# Returns whether the symbol is defined in the passed <FileName>. -# -sub IsDefinedIn #(file) - { - my ($self, $file) = @_; - return ($self->IsDefined() && exists $self->[DEFINITIONS]{$file}); - }; - - -# -# Function: Definitions -# -# Returns an array of all the <FileNames> that define this symbol. If none do, will return an empty array. -# -sub Definitions - { - my $self = shift; - - if ($self->IsDefined()) - { return keys %{$self->[DEFINITIONS]}; } - else - { return ( ); }; - }; - - -# -# Function: GlobalDefinition -# -# Returns the <FileName> that contains the global definition of this symbol, or undef if the symbol isn't defined. -# -sub GlobalDefinition - { - return $_[0]->[GLOBAL_DEFINITION]; - }; - - -# -# Function: TypeDefinedIn -# -# Returns the <TopicType> of the symbol defined in the passed <FileName>, or undef if it's not defined in that file. -# -sub TypeDefinedIn #(file) - { - my ($self, $file) = @_; - - if ($self->IsDefined()) - { return $self->[DEFINITIONS]{$file}->Type(); } - else - { return undef; }; - }; - - -# -# Function: GlobalType -# -# Returns the <TopicType> of the global definition, or undef if the symbol isn't defined. -# -sub GlobalType - { - my $self = shift; - - my $globalDefinition = $self->GlobalDefinition(); - - if (!defined $globalDefinition) - { return undef; } - else - { return $self->[DEFINITIONS]{$globalDefinition}->Type(); }; - }; - - -# -# Function: PrototypeDefinedIn -# -# Returns the prototype of symbol defined in the passed <FileName>, or undef if it doesn't exist or is not defined in that file. -# -sub PrototypeDefinedIn #(file) - { - my ($self, $file) = @_; - - if ($self->IsDefined()) - { return $self->[DEFINITIONS]{$file}->Prototype(); } - else - { return undef; }; - }; - - -# -# Function: GlobalPrototype -# -# Returns the prototype of the global definition. Will be undef if it doesn't exist or the symbol isn't defined. -# -sub GlobalPrototype - { - my $self = shift; - - my $globalDefinition = $self->GlobalDefinition(); - - if (!defined $globalDefinition) - { return undef; } - else - { return $self->[DEFINITIONS]{$globalDefinition}->Prototype(); }; - }; - - -# -# Function: SummaryDefinedIn -# -# Returns the summary of symbol defined in the passed <FileName>, or undef if it doesn't exist or is not defined in that file. -# -sub SummaryDefinedIn #(file) - { - my ($self, $file) = @_; - - if ($self->IsDefined()) - { return $self->[DEFINITIONS]{$file}->Summary(); } - else - { return undef; }; - }; - - -# -# Function: GlobalSummary -# -# Returns the summary of the global definition. Will be undef if it doesn't exist or the symbol isn't defined. -# -sub GlobalSummary - { - my $self = shift; - - my $globalDefinition = $self->GlobalDefinition(); - - if (!defined $globalDefinition) - { return undef; } - else - { return $self->[DEFINITIONS]{$globalDefinition}->Summary(); }; - }; - - -# -# Function: HasReferences -# -# Returns whether the symbol can be interpreted as any references. -# -sub HasReferences - { - return defined $_[0]->[REFERENCES]; - }; - -# -# Function: References -# -# Returns an array of all the reference strings that can be interpreted as this symbol. If none, will return an empty array. -# -sub References - { - if (defined $_[0]->[REFERENCES]) - { return keys %{$_[0]->[REFERENCES]}; } - else - { return ( ); }; - }; - - -# -# Function: ReferencesAndScores -# -# Returns a hash of all the references that can be interpreted as this symbol and their scores. The keys are the reference -# strings, and the values are the scores. If none, will return an empty hash. -# -sub ReferencesAndScores - { - if (defined $_[0]->[REFERENCES]) - { return %{$_[0]->[REFERENCES]}; } - else - { return ( ); }; - }; - -1; diff --git a/docs/doctool/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm b/docs/doctool/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm deleted file mode 100644 index fddff164..00000000 --- a/docs/doctool/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm +++ /dev/null @@ -1,96 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::SymbolTable::SymbolDefinition -# -############################################################################### -# -# A class representing a symbol definition. This does not store the definition symbol, class, or file. -# -############################################################################### - -# 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::SymbolTable::SymbolDefinition; - - -############################################################################### -# Group: Implementation - -# -# Constants: Members -# -# The class is implemented as a blessed arrayref. The following constants are its members. -# -# TYPE - The symbol <TopicType>. -# PROTOTYPE - The symbol's prototype, if applicable. Will be undef otherwise. -# SUMMARY - The symbol's summary, if applicable. Will be undef otherwise. -# -use constant TYPE => 0; -use constant PROTOTYPE => 1; -use constant SUMMARY => 2; -# New depends on the order of the constants. - - -############################################################################### -# Group: Functions - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# type - The symbol <TopicType>. -# prototype - The symbol prototype, if applicable. Undef otherwise. -# summary - The symbol's summary, if applicable. Undef otherwise. -# -sub New #(type, prototype, summary) - { - # This depends on the parameter list being the same as the constant order. - - my $package = shift; - - my $object = [ @_ ]; - bless $object, $package; - - return $object; - }; - - -# Function: Type -# Returns the definition's <TopicType>. -sub Type - { return $_[0]->[TYPE]; }; - -# Function: SetType -# Changes the <TopicType>. -sub SetType #(type) - { $_[0]->[TYPE] = $_[1]; }; - -# Function: Prototype -# Returns the definition's prototype, or undef if it doesn't have one. -sub Prototype - { return $_[0]->[PROTOTYPE]; }; - -# Function: SetPrototype -# Changes the prototype. -sub SetPrototype #(prototype) - { $_[0]->[PROTOTYPE] = $_[1]; }; - -# Function: Summary -# Returns the definition's summary, or undef if it doesn't have one. -sub Summary - { return $_[0]->[SUMMARY]; }; - -# Function: SetSummary -# Changes the summary. -sub SetSummary #(summary) - { $_[0]->[SUMMARY] = $_[1]; }; - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Topics.pm b/docs/doctool/Modules/NaturalDocs/Topics.pm deleted file mode 100644 index 24418bdd..00000000 --- a/docs/doctool/Modules/NaturalDocs/Topics.pm +++ /dev/null @@ -1,1351 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Topics -# -############################################################################### -# -# The topic constants and functions to convert them to and from strings used throughout the script. All constants are exported -# by default. -# -############################################################################### - -# This file is part of Natural Docs, which is Copyright (C) 2003-2005 Greg Valure -# Natural Docs is licensed under the GPL - -use Text::Wrap ( ); -use Tie::RefHash ( ); - -use strict; -use integer; - -use NaturalDocs::Topics::Type; - -package NaturalDocs::Topics; - -use base 'Exporter'; -our @EXPORT = ( 'TOPIC_GENERAL', 'TOPIC_GENERIC', 'TOPIC_GROUP', 'TOPIC_CLASS', 'TOPIC_FILE', 'TOPIC_FUNCTION', - 'TOPIC_VARIABLE', 'TOPIC_PROPERTY', 'TOPIC_TYPE', 'TOPIC_ENUMERATION', 'TOPIC_CONSTANT', - 'TOPIC_INTERFACE', 'TOPIC_EVENT', 'TOPIC_DELEGATE', 'TOPIC_SECTION' ); - - - -############################################################################### -# Group: Types - - -# -# Type: TopicType -# -# A string representing a topic type as defined in <Topics.txt>. It's format should be treated as opaque; use <MakeTopicType()> -# to get them from topic names. However, they can be compared for equality with string functions. -# - - -# -# Constants: Default TopicTypes -# -# Exported constants of the default <TopicTypes>, so you don't have to go through <TypeFromName()> every time. -# -# TOPIC_GENERAL - The general <TopicType>, which has the special meaning of none in particular. -# TOPIC_GENERIC - Generic <TopicType>. -# TOPIC_GROUP - Group <TopicType>. -# TOPIC_CLASS - Class <TopicType>. -# TOPIC_INTERFACE - Interface <TopicType>. -# TOPIC_FILE - File <TopicType>. -# TOPIC_SECTION - Section <TopicType>. -# TOPIC_FUNCTION - Function <TopicType>. -# TOPIC_VARIABLE - Variable <TopicType>. -# TOPIC_PROPERTY - Property <TopicType>. -# TOPIC_TYPE - Type <TopicType>. -# TOPIC_CONSTANT - Constant <TopicType>. -# TOPIC_ENUMERATION - Enum <TopicType>. -# TOPIC_DELEGATE - Delegate <TopicType>. -# TOPIC_EVENT - Event <TopicType>. -# -use constant TOPIC_GENERAL => 'general'; -use constant TOPIC_GENERIC => 'generic'; -use constant TOPIC_GROUP => 'group'; -use constant TOPIC_CLASS => 'class'; -use constant TOPIC_INTERFACE => 'interface'; -use constant TOPIC_FILE => 'file'; -use constant TOPIC_SECTION => 'section'; -use constant TOPIC_FUNCTION => 'function'; -use constant TOPIC_VARIABLE => 'variable'; -use constant TOPIC_PROPERTY => 'property'; -use constant TOPIC_TYPE => 'type'; -use constant TOPIC_CONSTANT => 'constant'; -use constant TOPIC_ENUMERATION => 'enumeration'; -use constant TOPIC_DELEGATE => 'delegate'; -use constant TOPIC_EVENT => 'event'; -# Dependency: The values of these constants must match what is generated by MakeTopicType(). -# Dependency: These types must be added to requiredTypeNames so that they always exist. - - - - -############################################################################### -# Group: Variables - - -# -# handle: FH_TOPICS -# -# The file handle used when writing to <Topics.txt>. -# - - -# -# hash: types -# -# A hashref that maps <TopicTypes> to <NaturalDocs::Topics::Type>s. -# -my %types; - - -# -# hash: names -# -# A hashref that maps various forms of the all-lowercase type names to <TopicTypes>. All are in the same hash because -# two names that reduce to the same thing it would cause big problems, and we need to catch that. Keys include -# -# - Topic names -# - Plural topic names -# - Alphanumeric-only topic names -# - Alphanumeric-only plural topic names -# -my %names; - - -# -# hash: keywords -# -# A hashref that maps all-lowercase keywords to their <TopicTypes>. Must not have any of the same keys as -# <pluralKeywords>. -# -my %keywords; - - -# -# hash: pluralKeywords -# -# A hashref that maps all-lowercase plural keywords to their <TopicTypes>. Must not have any of the same keys as -# <keywords>. -# -my %pluralKeywords; - - -# -# hash: indexable -# -# An existence hash of all the indexable <TopicTypes>. -# -my %indexable; - - -# -# array: requiredTypeNames -# -# An array of the <TopicType> names which are required to be defined in the main file. Are in the order they should appear -# when reformatting. -# -my @requiredTypeNames = ( 'Generic', 'Class', 'Interface', 'Section', 'File', 'Group', 'Function', 'Variable', 'Property', 'Type', - 'Constant', 'Enumeration', 'Event', 'Delegate' ); - - -# -# array: legacyTypes -# -# An array that converts the legacy topic types, which were numeric constants prior to 1.3, to the current <TopicTypes>. -# The legacy types are used as an index into the array. Note that this does not support list type values. -# -my @legacyTypes = ( TOPIC_GENERAL, TOPIC_CLASS, TOPIC_SECTION, TOPIC_FILE, TOPIC_GROUP, TOPIC_FUNCTION, - TOPIC_VARIABLE, TOPIC_GENERIC, TOPIC_TYPE, TOPIC_CONSTANT, TOPIC_PROPERTY ); - - -# -# array: mainTopicNames -# -# An array of the <TopicType> names that are defined in the main <Topics.txt>. -# -my @mainTopicNames; - - - -############################################################################### -# Group: Files - - -# -# File: Topics.txt -# -# The configuration file that defines or overrides the topic definitions for Natural Docs. One version sits in Natural Docs' -# configuration directory, and another can be in a project directory to add to or override them. -# -# > # [comments] -# -# Everything after a # symbol is ignored. -# -# Except when specifying topic names, everything below is case-insensitive. -# -# > Format: [version] -# -# Specifies the file format version of the file. -# -# -# Sections: -# -# > Ignore[d] Keyword[s]: [keyword], [keyword] ... -# > [keyword] -# > [keyword], [keyword] -# > ... -# -# Ignores the keywords so that they're not recognized as Natural Docs topics anymore. Can be specified as a list on the same -# line and/or following like a normal Keywords section. -# -# > Topic Type: [name] -# > Alter Topic Type: [name] -# -# Creates a new topic type or alters an existing one. The name can only contain <CFChars> and isn't case sensitive, although -# the original case is remembered for presentation. -# -# The name General is reserved. There are a number of default types that must be defined in the main file as well, but those -# are governed by <NaturalDocs::Topics> and are not included here. The default types can have their keywords or behaviors -# changed, though, either by editing the default file or by overriding them in the user file. -# -# Enumeration is a special type. It is indexed with Types and its definition list members are listed with Constants according -# to the rules in <Languages.txt>. -# -# -# Topic Type Sections: -# -# > Plural: [name] -# -# Specifies the plural name of the topic type. Defaults to the singular name. Has the same restrictions as the topic type -# name. -# -# > Index: [yes|no] -# -# Whether the topic type gets an index. Defaults to yes. -# -# > Scope: [normal|start|end|always global] -# -# How the topic affects scope. Defaults to normal. -# -# normal - The topic stays within the current scope. -# start - The topic starts a new scope for all the topics beneath it, like class topics. -# end - The topic resets the scope back to global for all the topics beneath it, like section topics. -# always global - The topic is defined as a global symbol, but does not change the scope for any other topics. -# -# > Class Hierarchy: [yes|no] -# -# Whether the topic is part of the class hierarchy. Defaults to no. -# -# > Variable Type: [yes|no] -# -# Whether the topic can be used as a variable type. Defaults to no. -# -# > Page Title if First: [yes|no] -# -# Whether the title of this topic becomes the page title if it is the first topic in a file. Defaults to no. -# -# > Break Lists: [yes|no] -# -# Whether list topics should be broken into individual topics in the output. Defaults to no. -# -# > Can Group With: [topic type], [topic type], ... -# -# The list of <TopicTypes> the topic can possibly be grouped with. -# -# > [Add] Keyword[s]: -# > [keyword] -# > [keyword], [plural keyword] -# > ... -# -# A list of the topic type's keywords. Each line after the heading is the keyword and optionally its plural form. This continues -# until the next line in "keyword: value" format. "Add" isn't required. -# -# - Keywords can only have letters and numbers. No punctuation or spaces are allowed. -# - Keywords are not case sensitive. -# - Subsequent keyword sections add to the list. They don't replace it. -# - Keywords can be redefined by other keyword sections. -# -# -# Revisions: -# -# 1.4: -# -# Added Variable Type. -# -# 1.3: -# -# The initial version of this file. -# - - -############################################################################### -# Group: File Functions - - -# -# Function: Load -# -# Loads both the master and the project version of <Topics.txt>. -# -sub Load - { - my $self = shift; - - # Add the special General topic type. - - $types{::TOPIC_GENERAL()} = NaturalDocs::Topics::Type->New('General', 'General', 1, ::SCOPE_NORMAL(), undef); - $names{'general'} = ::TOPIC_GENERAL(); - $indexable{::TOPIC_GENERAL()} = 1; - # There are no keywords for the general topic. - - - $self->LoadFile(1); # Main - - # Dependency: All the default topic types must be checked for existence. - - # Check to see if the required types are defined. - foreach my $name (@requiredTypeNames) - { - if (!exists $names{lc($name)}) - { NaturalDocs::ConfigFile->AddError('The ' . $name . ' topic type must be defined in the main topics file.'); }; - }; - - my $errorCount = NaturalDocs::ConfigFile->ErrorCount(); - - if ($errorCount) - { - NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile(); - NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors') - . ' in ' . NaturalDocs::Project->MainTopicsFile()); - } - - - $self->LoadFile(); # User - - $errorCount = NaturalDocs::ConfigFile->ErrorCount(); - - if ($errorCount) - { - NaturalDocs::ConfigFile->PrintErrorsAndAnnotateFile(); - NaturalDocs::Error->SoftDeath('There ' . ($errorCount == 1 ? 'is an error' : 'are ' . $errorCount . ' errors') - . ' in ' . NaturalDocs::Project->UserTopicsFile()); - } - }; - - -# -# Function: LoadFile -# -# Loads a particular version of <Topics.txt>. -# -# Parameters: -# -# isMain - Whether the file is the main file or not. -# -sub LoadFile #(isMain) - { - my ($self, $isMain) = @_; - - my ($file, $status); - - if ($isMain) - { - $file = NaturalDocs::Project->MainTopicsFile(); - $status = NaturalDocs::Project->MainTopicsFileStatus(); - } - else - { - $file = NaturalDocs::Project->UserTopicsFile(); - $status = NaturalDocs::Project->UserTopicsFileStatus(); - }; - - my $version; - - if ($version = NaturalDocs::ConfigFile->Open($file)) - { - # The format hasn't changed since the file was introduced. - - if ($status == ::FILE_CHANGED()) - { NaturalDocs::Project->ReparseEverything(); }; - - my ($topicTypeKeyword, $topicTypeName, $topicType, $topicTypeObject, $inKeywords, $inIgnoredKeywords); - - # Keys are topic type objects, values are unparsed strings. - my %canGroupWith; - tie %canGroupWith, 'Tie::RefHash'; - - while (my ($keyword, $value) = NaturalDocs::ConfigFile->GetLine()) - { - if ($keyword) - { - $inKeywords = 0; - $inIgnoredKeywords = 0; - }; - - if ($keyword eq 'topic type') - { - $topicTypeKeyword = $keyword; - $topicTypeName = $value; - - # Resolve conflicts and create the type if necessary. - - $topicType = $self->MakeTopicType($topicTypeName); - my $lcTopicTypeName = lc($topicTypeName); - - my $lcTopicTypeAName = $lcTopicTypeName; - $lcTopicTypeAName =~ tr/a-z0-9//cd; - - if (!NaturalDocs::ConfigFile->HasOnlyCFChars($topicTypeName)) - { - NaturalDocs::ConfigFile->AddError('Topic names can only have ' . NaturalDocs::ConfigFile->CFCharNames() . '.'); - } - elsif ($topicType eq ::TOPIC_GENERAL()) - { - NaturalDocs::ConfigFile->AddError('You cannot define a General topic type.'); - } - elsif (defined $types{$topicType} || defined $names{$lcTopicTypeName} || defined $names{$lcTopicTypeAName}) - { - NaturalDocs::ConfigFile->AddError('Topic type ' . $topicTypeName . ' is already defined or its name is too ' - . 'similar to an existing name. Use Alter Topic Type if you meant to override ' - . 'its settings.'); - } - else - { - $topicTypeObject = NaturalDocs::Topics::Type->New($topicTypeName, $topicTypeName, 1, ::SCOPE_NORMAL(), - 0, 0); - - $types{$topicType} = $topicTypeObject; - $names{$lcTopicTypeName} = $topicType; - $names{$lcTopicTypeAName} = $topicType; - - $indexable{$topicType} = 1; - - if ($isMain) - { push @mainTopicNames, $topicTypeName; }; - }; - } - - elsif ($keyword eq 'alter topic type') - { - $topicTypeKeyword = $keyword; - $topicTypeName = $value; - - # Resolve conflicts and create the type if necessary. - - $topicType = $names{lc($topicTypeName)}; - - if (!defined $topicType) - { NaturalDocs::ConfigFile->AddError('Topic type ' . $topicTypeName . ' doesn\'t exist.'); } - elsif ($topicType eq ::TOPIC_GENERAL()) - { NaturalDocs::ConfigFile->AddError('You cannot alter the General topic type.'); } - else - { - $topicTypeObject = $types{$topicType}; - }; - } - - elsif ($keyword =~ /^ignored? keywords?$/) - { - $inIgnoredKeywords = 1; - - my @ignoredKeywords = split(/ ?, ?/, lc($value)); - - foreach my $ignoredKeyword (@ignoredKeywords) - { - delete $keywords{$ignoredKeyword}; - delete $pluralKeywords{$ignoredKeyword}; - }; - } - - # We continue even if there are errors in the topic type line so that we can find any other errors in the file as well. We'd - # rather them all show up at once instead of them showing up one at a time between Natural Docs runs. So we just ignore - # the settings if $topicTypeObject is undef. - - - elsif ($keyword eq 'plural') - { - my $pluralName = $value; - my $lcPluralName = lc($pluralName); - - my $lcPluralAName = $lcPluralName; - $lcPluralAName =~ tr/a-zA-Z0-9//cd; - - if (!NaturalDocs::ConfigFile->HasOnlyCFChars($pluralName)) - { - NaturalDocs::ConfigFile->AddError('Plural names can only have ' - . NaturalDocs::ConfigFile->CFCharNames() . '.'); - } - elsif ($lcPluralAName eq 'general') - { - NaturalDocs::ConfigFile->AddError('You cannot use General as a plural name for ' . $topicTypeName . '.'); - } - elsif ( (defined $names{$lcPluralName} && $names{$lcPluralName} ne $topicType) || - (defined $names{$lcPluralAName} && $names{$lcPluralAName} ne $topicType) ) - { - NaturalDocs::ConfigFile->AddError($topicTypeName . "'s plural name, " . $pluralName - . ', is already defined or is too similar to an existing name.'); - } - - elsif (defined $topicTypeObject) - { - $topicTypeObject->SetPluralName($pluralName); - - $names{$lcPluralName} = $topicType; - $names{$lcPluralAName} = $topicType; - }; - } - - elsif ($keyword eq 'index') - { - $value = lc($value); - - if ($value eq 'yes') - { - if (defined $topicTypeObject) - { - $topicTypeObject->SetIndex(1); - $indexable{$topicType} = 1; - }; - } - elsif ($value eq 'no') - { - if (defined $topicTypeObject) - { - $topicTypeObject->SetIndex(0); - delete $indexable{$topicType}; - }; - } - else - { - NaturalDocs::ConfigFile->AddError('Index lines can only be "yes" or "no".'); - }; - } - - elsif ($keyword eq 'class hierarchy') - { - $value = lc($value); - - if ($value eq 'yes') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetClassHierarchy(1); }; - } - elsif ($value eq 'no') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetClassHierarchy(0); }; - } - else - { - NaturalDocs::ConfigFile->AddError('Class Hierarchy lines can only be "yes" or "no".'); - }; - } - - elsif ($keyword eq 'variable type') - { - $value = lc($value); - - if ($value eq 'yes') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetVariableType(1); }; - } - elsif ($value eq 'no') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetVariableType(0); }; - } - else - { - NaturalDocs::ConfigFile->AddError('Variable Type lines can only be "yes" or "no".'); - }; - } - - elsif ($keyword eq 'scope') - { - $value = lc($value); - - if ($value eq 'normal') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetScope(::SCOPE_NORMAL()); }; - } - elsif ($value eq 'start') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetScope(::SCOPE_START()); }; - } - elsif ($value eq 'end') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetScope(::SCOPE_END()); }; - } - elsif ($value eq 'always global') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetScope(::SCOPE_ALWAYS_GLOBAL()); }; - } - else - { - NaturalDocs::ConfigFile->AddError('Scope lines can only be "normal", "start", "end", or "always global".'); - }; - } - - elsif ($keyword eq 'page title if first') - { - $value = lc($value); - - if ($value eq 'yes') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetPageTitleIfFirst(1); }; - } - elsif ($value eq 'no') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetPageTitleIfFirst(undef); }; - } - else - { - NaturalDocs::ConfigFile->AddError('Page Title if First lines can only be "yes" or "no".'); - }; - } - - elsif ($keyword eq 'break lists') - { - $value = lc($value); - - if ($value eq 'yes') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetBreakLists(1); }; - } - elsif ($value eq 'no') - { - if (defined $topicTypeObject) - { $topicTypeObject->SetBreakLists(undef); }; - } - else - { - NaturalDocs::ConfigFile->AddError('Break Lists lines can only be "yes" or "no".'); - }; - } - - elsif ($keyword eq 'can group with') - { - if (defined $topicTypeObject) - { $canGroupWith{$topicTypeObject} = lc($value); }; - } - - elsif ($keyword =~ /^(?:add )?keywords?$/) - { - $inKeywords = 1; - } - - elsif (defined $keyword) - { NaturalDocs::ConfigFile->AddError($keyword . ' is not a valid keyword.'); } - - elsif (!$inKeywords && !$inIgnoredKeywords) - { - NaturalDocs::ConfigFile->AddError('All lines in ' . $topicTypeKeyword . ' sections must begin with a keyword.'); - } - - else # No keyword but in keyword section. - { - $value = lc($value); - - if ($value =~ /^([a-z0-9 ]*[a-z0-9]) ?, ?([a-z0-9 ]+)$/) - { - my ($singular, $plural) = ($1, $2); - - if ($inIgnoredKeywords) - { - delete $keywords{$singular}; - delete $keywords{$plural}; - delete $pluralKeywords{$singular}; - delete $pluralKeywords{$plural}; - } - elsif (defined $topicTypeObject) - { - $keywords{$singular} = $topicType; - delete $pluralKeywords{$singular}; - - $pluralKeywords{$plural} = $topicType; - delete $keywords{$plural}; - }; - } - elsif ($value =~ /^[a-z0-9 ]+$/) - { - if ($inIgnoredKeywords) - { - delete $keywords{$value}; - delete $pluralKeywords{$value}; - } - elsif (defined $topicType) - { - $keywords{$value} = $topicType; - delete $pluralKeywords{$value}; - }; - } - else - { - NaturalDocs::ConfigFile->AddError('Keywords can only have letters, numbers, and spaces. ' - . 'Plurals must be separated by a comma.'); - }; - }; - }; - - NaturalDocs::ConfigFile->Close(); - - - # Parse out the Can Group With lines now that everything's defined. - - while (my ($typeObject, $value) = each %canGroupWith) - { - my @values = split(/ ?, ?/, $value); - my @types; - - foreach my $value (@values) - { - # We're just going to ignore invalid items. - if (exists $names{$value}) - { push @types, $names{$value}; }; - }; - - if (scalar @types) - { $typeObject->SetCanGroupWith(\@types); }; - }; - } - - else # couldn't open file - { - if ($isMain) - { die "Couldn't open topics file " . $file . "\n"; } - else - { NaturalDocs::Project->ReparseEverything(); }; - }; - }; - - -# -# Function: Save -# -# Saves the main and user versions of <Topics.txt>. -# -sub Save - { - my $self = shift; - - $self->SaveFile(1); # Main - $self->SaveFile(0); # User - }; - - -# -# Function: SaveFile -# -# Saves a particular version of <Topics.txt>. -# -# Parameters: -# -# isMain - Whether the file is the main file or not. -# -sub SaveFile #(isMain) - { - my ($self, $isMain) = @_; - - my $file; - - if ($isMain) - { - if (NaturalDocs::Project->MainTopicsFileStatus() == ::FILE_SAME()) - { return; }; - $file = NaturalDocs::Project->MainTopicsFile(); - } - else - { - # We have to check the main one two because this lists the topics defined in it. - if (NaturalDocs::Project->UserTopicsFileStatus() == ::FILE_SAME() && - NaturalDocs::Project->MainTopicsFileStatus() == ::FILE_SAME()) - { return; }; - $file = NaturalDocs::Project->UserTopicsFile(); - }; - - - # Array of topic type names in the order they appear in the file. If Alter Topic Type is used, the name will end with an asterisk. - my @topicTypeOrder; - - # Keys are topic type names, values are property hashrefs. Hashref keys are the property names, values the value. - # For keywords, the key is Keywords and the values are arrayrefs of singular and plural pairs. If no plural is defined, the entry - # will be undef. - my %properties; - - # List of ignored keywords specified as Ignore Keywords: [keyword], [keyword], ... - my @inlineIgnoredKeywords; - - # List of ignored keywords specified in [keyword], [plural keyword] lines. Done in pairs, like for regular keywords. - my @separateIgnoredKeywords; - - my $inIgnoredKeywords; - - if (NaturalDocs::ConfigFile->Open($file)) - { - # We can assume the file is valid. - - my ($keyword, $value, $topicTypeName); - - while (($keyword, $value) = NaturalDocs::ConfigFile->GetLine()) - { - $keyword = lc($keyword); - - if ($keyword eq 'topic type' || $keyword eq 'alter topic type') - { - $topicTypeName = $types{ $names{lc($value)} }->Name(); - - if ($keyword eq 'alter topic type') - { $topicTypeName .= '*'; }; - - push @topicTypeOrder, $topicTypeName; - - if (!exists $properties{$topicTypeName}) - { $properties{$topicTypeName} = { 'keywords' => [ ] }; }; - } - - elsif ($keyword eq 'plural') - { - $properties{$topicTypeName}->{$keyword} = $value; - } - - elsif ($keyword eq 'index' || - $keyword eq 'scope' || - $keyword eq 'page title if first' || - $keyword eq 'class hierarchy' || - $keyword eq 'variable type' || - $keyword eq 'break lists' || - $keyword eq 'can group with') - { - $properties{$topicTypeName}->{$keyword} = lc($value); - } - - elsif ($keyword =~ /^(?:add )?keywords?$/) - { - $inIgnoredKeywords = 0; - } - - elsif ($keyword =~ /^ignored? keywords?$/) - { - $inIgnoredKeywords = 1; - if ($value) - { push @inlineIgnoredKeywords, split(/ ?, ?/, $value); }; - } - - elsif (!$keyword) - { - my ($singular, $plural) = split(/ ?, ?/, lc($value)); - - if ($inIgnoredKeywords) - { push @separateIgnoredKeywords, $singular, $plural; } - else - { push @{$properties{$topicTypeName}->{'keywords'}}, $singular, $plural; }; - }; - }; - - NaturalDocs::ConfigFile->Close(); - }; - - - if (!open(FH_TOPICS, '>' . $file)) - { - # The main file may be on a shared volume or some other place the user doesn't have write access to. Since this is only to - # reformat the file, we can ignore the failure. - if ($isMain) - { return; } - else - { die "Couldn't save " . $file; }; - }; - - print FH_TOPICS 'Format: ' . NaturalDocs::Settings->TextAppVersion() . "\n\n"; - - # Remember the 80 character limit. - - if ($isMain) - { - print FH_TOPICS - "# This is the main Natural Docs topics file. If you change anything here, it\n" - . "# will apply to EVERY PROJECT you use Natural Docs on. If you'd like to\n" - . "# change something for just one project, edit the Topics.txt in its project\n" - . "# directory instead.\n"; - } - else - { - print FH_TOPICS - "# This is the Natural Docs topics file for this project. If you change anything\n" - . "# here, it will apply to THIS PROJECT ONLY. If you'd like to change something\n" - . "# for all your projects, edit the Topics.txt in Natural Docs' Config directory\n" - . "# instead.\n\n\n"; - - if (scalar @inlineIgnoredKeywords || scalar @separateIgnoredKeywords) - { - if (scalar @inlineIgnoredKeywords == 1 && !scalar @separateIgnoredKeywords) - { - print FH_TOPICS 'Ignore Keyword: ' . $inlineIgnoredKeywords[0] . "\n"; - } - else - { - print FH_TOPICS - 'Ignore Keywords: ' . join(', ', @inlineIgnoredKeywords) . "\n"; - - for (my $i = 0; $i < scalar @separateIgnoredKeywords; $i += 2) - { - print FH_TOPICS ' ' . $separateIgnoredKeywords[$i]; - - if (defined $separateIgnoredKeywords[$i + 1]) - { print FH_TOPICS ', ' . $separateIgnoredKeywords[$i + 1]; }; - - print FH_TOPICS "\n"; - }; - }; - } - else - { - print FH_TOPICS - "# If you'd like to prevent keywords from being recognized by Natural Docs, you\n" - . "# can do it like this:\n" - . "# Ignore Keywords: [keyword], [keyword], ...\n" - . "#\n" - . "# Or you can use the list syntax like how they are defined:\n" - . "# Ignore Keywords:\n" - . "# [keyword]\n" - . "# [keyword], [plural keyword]\n" - . "# ...\n"; - }; - }; - - print FH_TOPICS # [CFChars] - "\n\n" - . "#-------------------------------------------------------------------------------\n" - . "# SYNTAX:\n" - . "#\n"; - - if ($isMain) - { - print FH_TOPICS - "# Topic Type: [name]\n" - . "# Creates a new topic type. Each type gets its own index and behavior\n" - . "# settings. Its name can have letters, numbers, spaces, and these\n" - . "# charaters: - / . '\n" - . "#\n" - . "# The Enumeration type is special. It's indexed with Types but its members\n" - . "# are indexed with Constants according to the rules in Languages.txt.\n" - . "#\n" - } - else - { - print FH_TOPICS - "# Topic Type: [name]\n" - . "# Alter Topic Type: [name]\n" - . "# Creates a new topic type or alters one from the main file. Each type gets\n" - . "# its own index and behavior settings. Its name can have letters, numbers,\n" - . "# spaces, and these charaters: - / . '\n" - . "#\n"; - }; - - print FH_TOPICS - "# Plural: [name]\n" - . "# Sets the plural name of the topic type, if different.\n" - . "#\n" - . "# Keywords:\n" - . "# [keyword]\n" - . "# [keyword], [plural keyword]\n" - . "# ...\n"; - - if ($isMain) - { - print FH_TOPICS - "# Defines a list of keywords for the topic type. They may only contain\n" - . "# letters, numbers, and spaces and are not case sensitive. Plural keywords\n" - . "# are used for list topics.\n"; - } - else - { - print FH_TOPICS - "# Defines or adds to the list of keywords for the topic type. They may only\n" - . "# contain letters, numbers, and spaces and are not case sensitive. Plural\n" - . "# keywords are used for list topics. You can redefine keywords found in the\n" - . "# main topics file.\n"; - } - - print FH_TOPICS - "#\n" - . "# Index: [yes|no]\n" - . "# Whether the topics get their own index. Defaults to yes. Everything is\n" - . "# included in the general index regardless of this setting.\n" - . "#\n" - . "# Scope: [normal|start|end|always global]\n" - . "# How the topics affects scope. Defaults to normal.\n" - . "# normal - Topics stay within the current scope.\n" - . "# start - Topics start a new scope for all the topics beneath it,\n" - . "# like class topics.\n" - . "# end - Topics reset the scope back to global for all the topics\n" - . "# beneath it.\n" - . "# always global - Topics are defined as global, but do not change the scope\n" - . "# for any other topics.\n" - . "#\n" - . "# Class Hierarchy: [yes|no]\n" - . "# Whether the topics are part of the class hierarchy. Defaults to no.\n" - . "#\n" - . "# Variable Type: [yes|no]\n" - . "# Whether the topics can be a variable type. Defaults to no.\n" - . "#\n" - . "# Page Title If First: [yes|no]\n" - . "# Whether the topic's title becomes the page title if it's the first one in\n" - . "# a file. Defaults to no.\n" - . "#\n" - . "# Break Lists: [yes|no]\n" - . "# Whether list topics should be broken into individual topics in the output.\n" - . "# Defaults to no.\n" - . "#\n" - . "# Can Group With: [type], [type], ...\n" - . "# Defines a list of topic types that this one can possibly be grouped with.\n" - . "# Defaults to none.\n" - . "#-------------------------------------------------------------------------------\n\n"; - - my $listToPrint; - - if ($isMain) - { - print FH_TOPICS - "# The following topics MUST be defined in this file:\n" - . "#\n"; - $listToPrint = \@requiredTypeNames; - } - else - { - print FH_TOPICS - "# The following topics are defined in the main file, if you'd like to alter\n" - . "# their behavior or add keywords:\n" - . "#\n"; - $listToPrint = \@mainTopicNames; - } - - print FH_TOPICS - Text::Wrap::wrap('# ', '# ', join(', ', @$listToPrint)) . "\n" - . "\n" - . "# If you add something that you think would be useful to other developers\n" - . "# and should be included in Natural Docs by default, please e-mail it to\n" - . "# topics [at] naturaldocs [dot] org.\n"; - - # Existence hash. We do this because we want the required ones to go first by adding them to @topicTypeOrder, but we don't - # want them to appear twice. - my %doneTopicTypes; - my ($altering, $numberOfProperties); - - if ($isMain) - { unshift @topicTypeOrder, @requiredTypeNames; }; - - my @propertyOrder = ('Plural', 'Index', 'Scope', 'Class Hierarchy', 'Variable Type', 'Page Title If First', 'Break Lists'); - - foreach my $topicType (@topicTypeOrder) - { - if (!exists $doneTopicTypes{$topicType}) - { - if (substr($topicType, -1) eq '*') - { - print FH_TOPICS "\n\n" - . 'Alter Topic Type: ' . substr($topicType, 0, -1) . "\n\n"; - - $altering = 1; - $numberOfProperties = 0; - } - else - { - print FH_TOPICS "\n\n" - . 'Topic Type: ' . $topicType . "\n\n"; - - $altering = 0; - $numberOfProperties = 0; - }; - - foreach my $property (@propertyOrder) - { - if (exists $properties{$topicType}->{lc($property)}) - { - print FH_TOPICS - ' ' . $property . ': ' . ucfirst( $properties{$topicType}->{lc($property)} ) . "\n"; - - $numberOfProperties++; - }; - }; - - if (exists $properties{$topicType}->{'can group with'}) - { - my @typeStrings = split(/ ?, ?/, lc($properties{$topicType}->{'can group with'})); - my @types; - - foreach my $typeString (@typeStrings) - { - if (exists $names{$typeString}) - { push @types, $names{$typeString}; }; - }; - - if (scalar @types) - { - for (my $i = 0; $i < scalar @types; $i++) - { - my $name = NaturalDocs::Topics->NameOfType($types[$i], 1); - - if ($i == 0) - { print FH_TOPICS ' Can Group With: ' . $name; } - else - { print FH_TOPICS ', ' . $name; }; - }; - - print FH_TOPICS "\n"; - $numberOfProperties++; - }; - }; - - if (scalar @{$properties{$topicType}->{'keywords'}}) - { - if ($numberOfProperties > 1) - { print FH_TOPICS "\n"; }; - - print FH_TOPICS - ' ' . ($altering ? 'Add ' : '') . 'Keywords:' . "\n"; - - my $keywords = $properties{$topicType}->{'keywords'}; - - for (my $i = 0; $i < scalar @$keywords; $i += 2) - { - print FH_TOPICS ' ' . $keywords->[$i]; - - if (defined $keywords->[$i + 1]) - { print FH_TOPICS ', ' . $keywords->[$i + 1]; }; - - print FH_TOPICS "\n"; - }; - }; - - $doneTopicTypes{$topicType} = 1; - }; - }; - - close(FH_TOPICS); - }; - - - -############################################################################### -# Group: Functions - - -# -# Function: KeywordInfo -# -# Returns information about a topic keyword. -# -# Parameters: -# -# keyword - The keyword, which may be plural. -# -# Returns: -# -# The array ( topicType, info, isPlural ), or an empty array if the keyword doesn't exist. -# -# topicType - The <TopicType> of the keyword. -# info - The <NaturalDocs::Topics::Type> of its type. -# isPlural - Whether the keyword was plural or not. -# -sub KeywordInfo #(keyword) - { - my ($self, $keyword) = @_; - - $keyword = lc($keyword); - - my $type = $keywords{$keyword}; - - if (defined $type) - { return ( $type, $types{$type}, undef ); }; - - $type = $pluralKeywords{$keyword}; - - if (defined $type) - { return ( $type, $types{$type}, 1 ); }; - - return ( ); - }; - - -# -# Function: NameInfo -# -# Returns information about a topic name. -# -# Parameters: -# -# name - The topic type name, which can be plural and/or alphanumeric only. -# -# Returns: -# -# The array ( topicType, info ), or an empty array if the name doesn't exist. Note that unlike <KeywordInfo()>, this -# does *not* tell you whether the name is plural or not. -# -# topicType - The <TopicType> of the name. -# info - The <NaturalDocs::Topics::Type> of the type. -# -sub NameInfo #(name) - { - my ($self, $name) = @_; - - my $type = $names{lc($name)}; - - if (defined $type) - { return ( $type, $types{$type} ); } - else - { return ( ); }; - }; - - -# -# Function: TypeInfo -# -# Returns information about a <TopicType>. -# -# Parameters: -# -# type - The <TopicType>. -# -# Returns: -# -# The <NaturalDocs::Topics::Type> of the type, or undef if it didn't exist. -# -sub TypeInfo #(type) - { - my ($self, $type) = @_; - return $types{$type}; - }; - - -# -# Function: NameOfType -# -# Returns the name of the passed <TopicType>, or undef if it doesn't exist. -# -# Parameters: -# -# topicType - The <TopicType>. -# plural - Whether to return the plural instead of the singular. -# alphanumericOnly - Whether to strips everything but alphanumeric characters out. Case isn't modified. -# -# Returns: -# -# The topic type name, according to what was specified in the parameters, or undef if it doesn't exist. -# -sub NameOfType #(topicType, plural, alphanumericOnly) - { - my ($self, $topicType, $plural, $alphanumericOnly) = @_; - - my $topicObject = $types{$topicType}; - - if (!defined $topicObject) - { return undef; }; - - my $topicName = ($plural ? $topicObject->PluralName() : $topicObject->Name()); - - if ($alphanumericOnly) - { $topicName =~ tr/a-zA-Z0-9//cd; }; - - return $topicName; - }; - - -# -# Function: TypeFromName -# -# Returns a <TopicType> for the passed topic name. -# -# Parameters: -# -# topicName - The name of the topic, which can be plural and/or alphanumeric only. -# -# Returns: -# -# The <TopicType>. It does not specify whether the name was plural or not. -# -sub TypeFromName #(topicName) - { - my ($self, $topicName) = @_; - - return $names{lc($topicName)}; - }; - - -# -# Function: IsValidType -# -# Returns whether the passed <TopicType> is defined. -# -sub IsValidType #(type) - { - my ($self, $type) = @_; - return exists $types{$type}; - }; - - -# -# Function: TypeFromLegacy -# -# Returns a <TopicType> for the passed legacy topic type integer. <TopicTypes> were changed from integer constants to -# strings in 1.3. -# -sub TypeFromLegacy #(legacyInt) - { - my ($self, $int) = @_; - return $legacyTypes[$int]; - }; - - -# -# Function: AllIndexableTypes -# -# Returns an array of all possible indexable <TopicTypes>. -# -sub AllIndexableTypes - { - my ($self) = @_; - return keys %indexable; - }; - - - -############################################################################### -# Group: Support Functions - - -# -# Function: MakeTopicType -# -# Returns a <TopicType> for the passed topic name. It does not check to see if it exists already. -# -# Parameters: -# -sub MakeTopicType #(topicName) - { - my ($self, $topicName) = @_; - - # Dependency: The values of the default topic type constants must match what is generated here. - - # Turn everything to lowercase and strip non-alphanumeric characters. - $topicName = lc($topicName); - $topicName =~ tr/a-z0-9//cd; - - return $topicName; - }; - - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Topics/Type.pm b/docs/doctool/Modules/NaturalDocs/Topics/Type.pm deleted file mode 100644 index c5558acb..00000000 --- a/docs/doctool/Modules/NaturalDocs/Topics/Type.pm +++ /dev/null @@ -1,155 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Topics::Type -# -############################################################################### -# -# A class storing information about a <TopicType>. -# -############################################################################### - -# 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::Topics::Type; - -use NaturalDocs::DefineMembers 'NAME', 'Name()', - 'PLURAL_NAME', 'PluralName()', 'SetPluralName()', - 'INDEX', 'Index()', 'SetIndex()', - 'SCOPE', 'Scope()', 'SetScope()', - 'PAGE_TITLE_IF_FIRST', 'PageTitleIfFirst()', 'SetPageTitleIfFirst()', - 'BREAK_LISTS', 'BreakLists()', 'SetBreakLists()', - 'CLASS_HIERARCHY', 'ClassHierarchy()', 'SetClassHierarchy()', - 'VARIABLE_TYPE', 'VariableType()', 'SetVariableType()', - 'CAN_GROUP_WITH'; - -# Dependency: New() depends on the order of these and that there are no parent classes. - -use base 'Exporter'; -our @EXPORT = ('SCOPE_NORMAL', 'SCOPE_START', 'SCOPE_END', 'SCOPE_ALWAYS_GLOBAL'); - -# -# Constants: Members -# -# The object is implemented as a blessed arrayref, with the following constants as its indexes. -# -# NAME - The topic's name. -# PLURAL_NAME - The topic's plural name. -# INDEX - Whether the topic is indexed. -# SCOPE - The topic's <ScopeType>. -# PAGE_TITLE_IF_FIRST - Whether the topic becomes the page title if it's first in a file. -# BREAK_LISTS - Whether list topics should be broken into individual topics in the output. -# CLASS_HIERARCHY - Whether the topic is part of the class hierarchy. -# VARIABLE_TYPE - Whether the topic can be a variable type. -# CAN_GROUP_WITH - The existence hashref of <TopicTypes> the type can be grouped with. -# - - - -############################################################################### -# Group: Types - - -# -# Constants: ScopeType -# -# The possible values for <Scope()>. -# -# SCOPE_NORMAL - The topic stays in the current scope without affecting it. -# SCOPE_START - The topic starts a scope. -# SCOPE_END - The topic ends a scope, returning it to global. -# SCOPE_ALWAYS_GLOBAL - The topic is always global, but it doesn't affect the current scope. -# -use constant SCOPE_NORMAL => 1; -use constant SCOPE_START => 2; -use constant SCOPE_END => 3; -use constant SCOPE_ALWAYS_GLOBAL => 4; - - - -############################################################################### -# Group: Functions - - -# -# Function: New -# -# Creates and returns a new object. -# -# Parameters: -# -# name - The topic name. -# pluralName - The topic's plural name. -# index - Whether the topic is indexed. -# scope - The topic's <ScopeType>. -# pageTitleIfFirst - Whether the topic becomes the page title if it's the first one in a file. -# breakLists - Whether list topics should be broken into individual topics in the output. -# -sub New #(name, pluralName, index, scope, pageTitleIfFirst, breakLists) - { - my ($self, @params) = @_; - - # Dependency: Depends on the parameter order matching the member order and that there are no parent classes. - - my $object = [ @params ]; - bless $object, $self; - - return $object; - }; - - -# -# Functions: Accessors -# -# Name - Returns the topic name. -# PluralName - Returns the topic's plural name. -# SetPluralName - Replaces the topic's plural name. -# Index - Whether the topic is indexed. -# SetIndex - Sets whether the topic is indexed. -# Scope - Returns the topic's <ScopeType>. -# SetScope - Replaces the topic's <ScopeType>. -# PageTitleIfFirst - Returns whether the topic becomes the page title if it's first in the file. -# SetPageTitleIfFirst - Sets whether the topic becomes the page title if it's first in the file. -# BreakLists - Returns whether list topics should be broken into individual topics in the output. -# SetBreakLists - Sets whether list topics should be broken into individual topics in the output. -# ClassHierarchy - Returns whether the topic is part of the class hierarchy. -# SetClassHierarchy - Sets whether the topic is part of the class hierarchy. -# VariableType - Returns whether the topic can be a variable type. -# SetVariableType - Sets whether the topic can be a variable type. -# - - -# -# Function: CanGroupWith -# -# Returns whether the type can be grouped with the passed <TopicType>. -# -sub CanGroupWith #(TopicType type) -> bool - { - my ($self, $type) = @_; - return ( defined $self->[CAN_GROUP_WITH] && exists $self->[CAN_GROUP_WITH]->{$type} ); - }; - - -# -# Function: SetCanGroupWith -# -# Sets the list of <TopicTypes> the type can be grouped with. -# -sub SetCanGroupWith #(TopicType[] types) - { - my ($self, $types) = @_; - - $self->[CAN_GROUP_WITH] = { }; - - foreach my $type (@$types) - { $self->[CAN_GROUP_WITH]->{$type} = 1; }; - }; - - - -1; diff --git a/docs/doctool/Modules/NaturalDocs/Version.pm b/docs/doctool/Modules/NaturalDocs/Version.pm deleted file mode 100644 index 67f1a331..00000000 --- a/docs/doctool/Modules/NaturalDocs/Version.pm +++ /dev/null @@ -1,201 +0,0 @@ -############################################################################### -# -# Package: NaturalDocs::Version -# -############################################################################### -# -# A package for handling version information. What? That's right. Although it should be easy and obvious, version numbers -# need to be dealt with in a variety of formats, plus there's compatibility with older releases which handled it differently. I -# wanted to centralize the code after it started getting complicated. So there ya go. -# -############################################################################### - -# 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::Version; - -# -# About: Format -# -# Version numbers are represented as major.minor. Major is from 0 to 255, and minor can have one or two digits. Minor is -# interpreted as a decimal, so 1.25 is less than 1.3. -# - -# Group: Functions - - -# -# Function: FromString -# -# Converts a version string to a <VersionInt>. -# -sub FromString #(string) - { - my ($self, $string) = @_; - - if ($string eq '1') - { - return 91; # 0.91 - } - else - { - $string =~ /^(\d+)\.(\d+)$/; - my ($major, $minor) = ($1, $2); - - if (length $minor == 1) - { $minor *= 10; }; - - return ($major << 8) | $minor; - }; - }; - -# -# Function: FromBinaryFile -# -# Retrieves a <VersionInt> from a binary file. -# -# Parameters: -# -# fileHandle - The handle of the file to read it from. It should be at the correct location. -# -# Returns: -# -# The <VersionInt>. -# -sub FromBinaryFile #(fileHandle) - { - my ($self, $fileHandle) = @_; - - my $version; - read($fileHandle, $version, 2); - - # A big-endian UInt16 as a shortcut to the integer format. - return unpack('n', $version); - }; - -# -# Function: FromTextFile -# -# Retrieves a <VersionInt> from a text file. -# -# Parameters: -# -# fileHandle - The handle of the file to read it from. It should be at the correct location. -# -# Returns: -# -# The <VersionInt>. -# -sub FromTextFile #(fileHandle) - { - my ($self, $fileHandle) = @_; - - my $version = <$fileHandle>; - ::XChomp(\$version); - - return $self->FromString($version); - }; - - -# -# Function: ToString -# -# Converts a <VersionInt> to a string. -# -sub ToString #(integer) - { - my ($self, $integer) = @_; - - my $major = $integer >> 8; - my $minor = $integer & 0x00FF; - - if ($minor % 10 == 0) - { $minor /= 10; } - elsif ($minor < 10) - { $minor = '0' . $minor; }; - - return $major . '.' . $minor; - }; - - -# -# Function: ToTextFile -# -# Writes a <VersionInt> to a text file. -# -# Parameters: -# -# fileHandle - The handle of the file to write it to. It should be at the correct location. -# version - The <VersionInt> to write. -# -sub ToTextFile #(fileHandle, version) - { - my ($self, $fileHandle, $version) = @_; - - print $fileHandle $self->ToString($version) . "\n"; - }; - - -# -# Function: ToBinaryFile -# -# Writes a <VersionInt> to a binary file. -# -# Parameters: -# -# fileHandle - The handle of the file to write it to. It should be at the correct location. -# version - The <VersionInt> to write. -# -sub ToBinaryFile #(fileHandle, version) - { - my ($self, $fileHandle, $version) = @_; - - # Big-endian UInt16 as a shortcut to the binary format. - print $fileHandle pack('n', $version); - }; - - - -############################################################################### -# Group: Implementation - -# -# About: String Format -# -# String versions are normally in the common major.minor format, with the exception of "1". -# -# If the string is "1" and not "1.0", it's represents releases 0.85 through 0.91, since those had a separate version number for data -# files. We switched to using the app version number in 0.95. This issue does not apply to binary data files since they came -# after 0.95. -# -# Text files with "1" as the version will be interpreted as 0.91, since this should not cause compatibility problems. The only -# file format changes between 0.85 and 0.91 were to <PreviousMenuState.nd>, which didn't exist in 0.85 and didn't change -# between 0.9 and 0.91, and <Menu.txt>, which only changed in 0.9 to add index entries. -# - -# -# About: Integer Format -# -# <VersionInts> are 16-bit unsigned values. The major version is the high-order byte, and the minor the low-order byte. -# The minor is always stored with two decimals, so 0.9 would be stored as 0 and 90. -# - -# -# About: Binary File Format -# -# In binary files, versions are two 8-bit unsigned values, appearing major then minor. The minor is always stored with two -# decimals, so 0.9 would be stored as 0 and 90. It's in the <Integer Format> if interpreted as a _big-endian_ 16-bit value. -# - -# -# About: Text File Format -# -# In text files, versions are the <String Format> followed by a native line break. -# - - -1; |