about summary refs log tree commit diff
path: root/docs/doctool/Modules/NaturalDocs
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-01-17 12:56:19 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-01-17 12:56:19 +0000
commite42c493d0c294ccb0a314c8447818c8d613195df (patch)
tree27e56d9415313ddccdb1550da64ed3ef80b1dcca /docs/doctool/Modules/NaturalDocs
parent037569c4e52f37196275dbafec670f54da249cf8 (diff)
downloadzcatch-e42c493d0c294ccb0a314c8447818c8d613195df.tar.gz
zcatch-e42c493d0c294ccb0a314c8447818c8d613195df.zip
removed olds docs
Diffstat (limited to 'docs/doctool/Modules/NaturalDocs')
-rw-r--r--docs/doctool/Modules/NaturalDocs/Builder.pm237
-rw-r--r--docs/doctool/Modules/NaturalDocs/Builder/Base.pm316
-rw-r--r--docs/doctool/Modules/NaturalDocs/Builder/FramedHTML.pm294
-rw-r--r--docs/doctool/Modules/NaturalDocs/Builder/HTML.pm363
-rw-r--r--docs/doctool/Modules/NaturalDocs/Builder/HTMLBase.pm3075
-rw-r--r--docs/doctool/Modules/NaturalDocs/ClassHierarchy.pm861
-rw-r--r--docs/doctool/Modules/NaturalDocs/ClassHierarchy/Class.pm412
-rw-r--r--docs/doctool/Modules/NaturalDocs/ClassHierarchy/File.pm157
-rw-r--r--docs/doctool/Modules/NaturalDocs/ConfigFile.pm497
-rw-r--r--docs/doctool/Modules/NaturalDocs/Constants.pm229
-rw-r--r--docs/doctool/Modules/NaturalDocs/DefineMembers.pm100
-rw-r--r--docs/doctool/Modules/NaturalDocs/Error.pm305
-rw-r--r--docs/doctool/Modules/NaturalDocs/File.pm521
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages.pm1471
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/ActionScript.pm885
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Ada.pm38
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Advanced.pm801
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Advanced/Scope.pm95
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm70
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Base.pm743
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/CSharp.pm1215
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/PLSQL.pm313
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Pascal.pm143
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Perl.pm1338
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Prototype.pm92
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Prototype/Parameter.pm74
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Simple.pm495
-rw-r--r--docs/doctool/Modules/NaturalDocs/Languages/Tcl.pm219
-rw-r--r--docs/doctool/Modules/NaturalDocs/Menu.pm3168
-rw-r--r--docs/doctool/Modules/NaturalDocs/Menu/Entry.pm201
-rw-r--r--docs/doctool/Modules/NaturalDocs/NDMarkup.pm76
-rw-r--r--docs/doctool/Modules/NaturalDocs/Parser.pm1209
-rw-r--r--docs/doctool/Modules/NaturalDocs/Parser/Native.pm926
-rw-r--r--docs/doctool/Modules/NaturalDocs/Parser/ParsedTopic.pm210
-rw-r--r--docs/doctool/Modules/NaturalDocs/Project.pm966
-rw-r--r--docs/doctool/Modules/NaturalDocs/Project/File.pm113
-rw-r--r--docs/doctool/Modules/NaturalDocs/ReferenceString.pm301
-rw-r--r--docs/doctool/Modules/NaturalDocs/Settings.pm1258
-rw-r--r--docs/doctool/Modules/NaturalDocs/Settings/BuildTarget.pm91
-rw-r--r--docs/doctool/Modules/NaturalDocs/StatusMessage.pm102
-rw-r--r--docs/doctool/Modules/NaturalDocs/SymbolString.pm208
-rw-r--r--docs/doctool/Modules/NaturalDocs/SymbolTable.pm1810
-rw-r--r--docs/doctool/Modules/NaturalDocs/SymbolTable/File.pm186
-rw-r--r--docs/doctool/Modules/NaturalDocs/SymbolTable/IndexElement.pm522
-rw-r--r--docs/doctool/Modules/NaturalDocs/SymbolTable/Reference.pm273
-rw-r--r--docs/doctool/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm97
-rw-r--r--docs/doctool/Modules/NaturalDocs/SymbolTable/Symbol.pm428
-rw-r--r--docs/doctool/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm96
-rw-r--r--docs/doctool/Modules/NaturalDocs/Topics.pm1351
-rw-r--r--docs/doctool/Modules/NaturalDocs/Topics/Type.pm155
-rw-r--r--docs/doctool/Modules/NaturalDocs/Version.pm201
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 .= '&nbsp;';  };
-
-            $output .=
-            '</td>';
-
-            for (my $i = 0; $i < scalar @$params; $i++)
-                {
-                if ($useCondensed)
-                    {
-                    $output .= '</tr><tr><td>&nbsp;&nbsp;&nbsp;</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/ $/&nbsp;/;
-
-                        $output .=
-                        '<td class=PTypePrefix nowrap>'
-                            . $htmlTypePrefix
-                        . '</td>';
-                        };
-
-                    if ($hasType)
-                        {
-                        $output .=
-                        '<td class=PType nowrap>'
-                            . $self->ConvertAmpChars($params->[$i]->Type()) . '&nbsp;'
-                        . '</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%' : '') . '>'
-                            . '&nbsp;' . $self->ConvertAmpChars( $typePrefix . $params->[$i]->Type() )
-                        . '</td>';
-                        };
-                    };
-
-                if ($hasDefaultValuePrefix)
-                    {
-                    $output .=
-                    '<td class=PDefaultValuePrefix>'
-                        . '&nbsp;' . $self->ConvertAmpChars( $params->[$i]->DefaultValuePrefix() ) . '&nbsp;'
-                    . '</td>';
-                    };
-
-                if ($hasDefaultValue)
-                    {
-                    $output .=
-                    '<td class=PDefaultValue width=100%>'
-                        . ($hasDefaultValuePrefix ? '' : '&nbsp;') . $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 .= '&nbsp;';  };
-
-            $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\)/&copy;/gi;
-        $footer =~ s/\(tm\)/&trade;/gi;
-        $footer =~ s/\(r\)/&reg;/gi;
-
-        $footer .= '&nbsp; 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 || '&nbsp;')
-            . '</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 .= ' &middot; ';  };
-
-        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/&/&amp;/g;
-    $string =~ s/</&lt;/g;
-    $string =~ s/>/&gt;/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/^\'/&lsquo;/gm;
-    $string =~ s/([\ \(\[\{])\'/$1&lsquo;/g;
-    $string =~ s/\'/&rsquo;/g;
-
-    $string =~ s/^\"/&ldquo;/gm;
-    $string =~ s/([\ \(\[\{])\"/$1&ldquo;/g;
-    $string =~ s/\"/&rdquo;/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/^\'/&lsquo;/gm;
-            $text =~ s/([\ \(\[\{])\'/$1&lsquo;/g;
-            $text =~ s/\'/&rsquo;/g;
-
-            $text =~ s/^&quot;/&ldquo;/gm;
-            $text =~ s/([\ \(\[\{])&quot;/$1&ldquo;/g;
-            $text =~ s/&quot;/&rdquo;/g;
-
-            # Copyright symbols.  Prevent conversion when part of (a), (b), (c) lists.
-            if ($text !~ /\(a\)/i)
-                {  $text =~ s/\(c\)/&copy;/gi;  };
-
-            # Trademark symbols.
-            $text =~ s/\(tm\)/&trade;/gi;
-            $text =~ s/\(r\)/&reg;/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 '&lt;' . $text . '&gt;';
-        };
-    };
-
-
-#
-#   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 &nbsp; 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.
-
-                      (&quot;|&[lr][sd]quo;|[\'\"\]\}\)]?)  # Tolerate closing quotes, parenthesis, etc.
-                      ((?:<[^>]+>)*)  # Tolerate tags
-
-                      \   # The space
-                      (?![a-z])  # Not followed by a lowercase character.
-
-                   /$1$2$3&nbsp;\ /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.
-
-                      \.
-
-                      (&quot;|&[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 '&nbsp; '; };
-        };
-
-    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/&/&amp;/g;
-    $text =~ s/\"/&quot;/g;
-    $text =~ s/</&lt;/g;
-    $text =~ s/>/&gt;/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/&/&amp;/g;
-    $text =~ s/</&lt;/g;
-    $text =~ s/>/&gt;/g;
-    $text =~ s/\"/&quot;/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/&quot;/\"/g;
-    $text =~ s/&gt;/>/g;
-    $text =~ s/&lt;/</g;
-    $text =~ s/&amp;/&/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>|[\.\!\?](?:[\)\}\'\ ]|&quot;|&gt;))/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 =~ /^(.*?)($|[\.\!\?](?:[\)\}\'\ ]|&quot;|&gt;))/)
-        {  $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 .= '&lt;';
-                };
-            }
-
-        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;