Ejemplo n.º 1
0
        // Group: Initialization Functions
        // __________________________________________________________________________


        /* Function: Start_Stage1
         *
         * Loads and combines the two versions of <Languages.txt>, returning whether it was successful.  If there were any
         * errors they will be added to errorList.
         *
         * Only the settings which don't depend on <Comments.txt> will be loaded.  Call <Start_Stage2()> after
         * <CommentTypes.Manager.Start_Stage1()> has been called to complete the process.
         *
         * Dependencies:
         *
         *		- <Config.Manager> must be started before this class can start.
         */
        public bool Start_Stage1(Errors.ErrorList errorList)
        {
            StartupIssues newStartupIssues = StartupIssues.None;
            bool          success          = true;


            //
            // Languages.txt
            //

            Path systemTextConfigPath  = EngineInstance.Config.SystemConfigFolder + "/Languages.txt";
            Path projectTextConfigPath = EngineInstance.Config.ProjectConfigFolder + "/Languages.txt";

            ConfigFiles.TextFileParser textConfigFileParser = new ConfigFiles.TextFileParser();

            // Load the system Languages.txt.
            if (!textConfigFileParser.Load(systemTextConfigPath, PropertySource.SystemLanguagesFile, errorList, out systemTextConfig))
            {
                success = false;
            }

            // Load the project Languages.txt.  We want to do this even if the system Languages.txt failed so we get the error messages
            // from both.
            if (System.IO.File.Exists(projectTextConfigPath))
            {
                if (!textConfigFileParser.Load(projectTextConfigPath, PropertySource.ProjectLanguagesFile, errorList, out projectTextConfig))
                {
                    success = false;
                }
            }
            // If it doesn't exist it's not an error.  Just create a blank config.
            else
            {
                projectTextConfig = new ConfigFiles.TextFile();
            }

            if (!success)
            {
                return(false);
            }

            if (!ValidateLanguages(systemTextConfig, errorList))
            {
                success = false;
            }
            if (!ValidateLanguages(projectTextConfig, errorList))
            {
                success = false;
            }

            if (!success)
            {
                return(false);
            }

            // Merge them into one combined config.  Note that this doesn't do file extensions, aliases, or shebang strings.  Only the
            // languages and their other properties will exist in the merged config.
            if (!MergeLanguages(systemTextConfig, projectTextConfig, out mergedTextConfig, errorList))
            {
                return(false);
            }
            if (!ValidateLanguages(mergedTextConfig, errorList))
            {
                return(false);
            }


            //
            // Languages.nd
            //

            Path lastRunConfigPath = EngineInstance.Config.WorkingDataFolder + "/Languages.nd";

            ConfigFiles.BinaryFileParser binaryConfigFileParser = new ConfigFiles.BinaryFileParser();

            // If we need to start fresh anyway we can skip loading the file and create a blank config
            if (EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                         StartupIssues.CommentIDsInvalidated |
                                         StartupIssues.CodeIDsInvalidated) ||

                // though if that wasn't the case but we failed at loading the file, the result is the same
                !binaryConfigFileParser.Load(lastRunConfigPath, out lastRunConfig))
            {
                lastRunConfig = new Config();

                newStartupIssues |= StartupIssues.NeedToReparseAllFiles |
                                    StartupIssues.CodeIDsInvalidated;
            }


            //
            // Create the final config
            //

            config = new Config();


            // We go through the languages present in the merged text config files and convert them into the final config.  We
            // need the contents of Comments.nd so we can keep the language IDs consistent from one run to the next if possible.

            IDObjects.NumberSet usedLanguageIDs        = new IDObjects.NumberSet();
            IDObjects.NumberSet lastRunUsedLanguageIDs = lastRunConfig.UsedLanguageIDs();

            if (mergedTextConfig.HasLanguages)
            {
                foreach (var textLanguage in mergedTextConfig.Languages)
                {
                    // First we need our base language object.  If there's a predefined language matching the name we'll use that so
                    // we get its settings and parser.  If not we'll create a new object.  We shouldn't have to worry about more than one
                    // language using the same predefined language object because validation should have taken care of that.
                    var finalLanguage = FindPredefinedLanguage(textLanguage.Name);

                    if (finalLanguage == null)
                    {
                        finalLanguage = new Language(textLanguage.Name);
                    }

                    // Apply the properties.  Keep going if there are errors since we want to find them all.
                    if (!FinalizeLanguage(ref finalLanguage, textLanguage, errorList))
                    {
                        success = false;
                    }

                    // We still need to set the ID.  See if a language of the same name existed in the previous run.
                    var lastRunLanguage = lastRunConfig.LanguageFromName(textLanguage.Name);

                    // If there wasn't one we can assign a new ID, but pick one that isn't used in this run or the last run so there's no
                    // conflicts.
                    if (lastRunLanguage == null)
                    {
                        int id = lastRunUsedLanguageIDs.LowestAvailable;

                        if (usedLanguageIDs.Contains(id))
                        {
                            id = Math.Max(usedLanguageIDs.Highest + 1, lastRunUsedLanguageIDs.Highest + 1);
                        }

                        finalLanguage.ID = id;
                        config.AddLanguage(finalLanguage);

                        usedLanguageIDs.Add(finalLanguage.ID);
                        lastRunUsedLanguageIDs.Add(finalLanguage.ID);
                    }
                    // If the language did exist but we haven't used its ID yet, we can keep it.
                    else if (!usedLanguageIDs.Contains(lastRunLanguage.ID))
                    {
                        finalLanguage.ID = lastRunLanguage.ID;
                        config.AddLanguage(finalLanguage);

                        usedLanguageIDs.Add(finalLanguage.ID);
                    }
                    // However, if the language did exist and we assigned that ID already, then we have a conflict and have to tell the
                    // engine that the IDs from the last run were invalidated.  We can just assign anything unused at this point since it
                    // no longer matters.
                    else
                    {
                        finalLanguage.ID = usedLanguageIDs.LowestAvailable;
                        config.AddLanguage(finalLanguage);

                        usedLanguageIDs.Add(finalLanguage.ID);
                        newStartupIssues |= StartupIssues.CodeIDsInvalidated;
                    }

                    // Now that we have a final ID, set the simple identifier for any type that still needs it.  Some types may have it null if
                    // it wasn't manually defined and the name didn't contain A-Z characters.
                    if (finalLanguage.SimpleIdentifier == null)
                    {
                        finalLanguage.SimpleIdentifier = "LanguageID" + finalLanguage.ID;
                    }

                    // Also create a generic parser object if one wasn't inherited from a predefined language.
                    if (!finalLanguage.HasParser)
                    {
                        finalLanguage.Parser = new Parser(EngineInstance, finalLanguage);
                    }
                }
            }

            if (!success)
            {
                return(false);
            }


            // Apply file extensions, aliases, and shebang strings now.

            MergeLanguageIdentifiersInto(ref config, systemTextConfig, projectTextConfig);


            // That's it for stage one.  Everything else is in stage 2.

            if (newStartupIssues != StartupIssues.None)
            {
                EngineInstance.AddStartupIssues(newStartupIssues);
            }

            return(true);
        }
Ejemplo n.º 2
0
        /* Function: Start
         *
         * Initializes the configuration and returns whether all the settings are correct and that execution is ready to begin.
         * If there are problems they are added as <Errors> to the errorList parameter.  This class is *not* designed to allow
         * multiple attempts.  If this function fails scrap the entire <Engine.Instance> and start again.
         *
         * After <Start()> is called the properties of this class become read-only.  This function will add all the input and filter
         * targets it has to <Files.Manager>, and all output targets to <Output.Manager>.
         */
        public bool Start(ErrorList errorList, ProjectConfig commandLineConfig)
        {
            bool success = true;


            // Validate project config folder

            projectConfigFolder = commandLineConfig.ProjectConfigFolder;

            if (!commandLineConfig.ProjectConfigFolderPropertyLocation.IsDefined ||
                String.IsNullOrEmpty(projectConfigFolder))
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.NoProjectConfigFolder"),
                    configSource: Source.CommandLine,
                    property: "ProjectConfigFolder"
                    );

                success = false;
            }

            else if (!System.IO.Directory.Exists(projectConfigFolder))
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.ProjectConfigFolderDoesntExist(name)", projectConfigFolder),
                    propertyLocation: commandLineConfig.ProjectConfigFolderPropertyLocation,
                    property: "ProjectConfigFolder"
                    );

                success = false;
            }

            else if (projectConfigFolder == SystemConfigFolder)
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.ProjectConfigFolderCannotEqualSystemConfigFolder"),
                    propertyLocation: commandLineConfig.ProjectConfigFolderPropertyLocation,
                    property: "ProjectConfigFolder"
                    );

                success = false;
            }

            if (success == false)
            {
                return(false);
            }


            // Load and merge configuration files

            ProjectConfig combinedConfig = new ProjectConfig(Source.Combined);

            MergeConfig(combinedConfig, commandLineConfig);

            var projectTxtParser = new Project_txt();

            if (System.IO.File.Exists(projectConfigFolder + "/Project.txt"))
            {
                ProjectConfig projectTxtConfig;

                if (projectTxtParser.Load(projectConfigFolder + "/Project.txt", out projectTxtConfig, errorList))
                {
                    MergeConfig(combinedConfig, projectTxtConfig);
                }
                else
                {
                    success = false;
                }
            }
            else if (System.IO.File.Exists(ProjectConfigFolder + "/Menu.txt"))
            {
                // Try to extract information from a pre-2.0 Menu.txt instead.

                ProjectConfig menuTxtConfig;
                var           menuTxtParser = new Menu_txt();

                if (menuTxtParser.Load(ProjectConfigFolder + "/Menu.txt", out menuTxtConfig))
                {
                    MergeConfig(combinedConfig, menuTxtConfig);
                }
                // No errors if this fails
            }
            // If neither file exists it's not an error condition.  Just treat it as if nothing was defined.

            MergeConfig(combinedConfig, systemDefaultConfig);

            if (success == false)
            {
                return(false);
            }


            // Validate the working data folder, creating one if it doesn't exist.

            workingDataFolder = combinedConfig.WorkingDataFolder;

            if (!combinedConfig.WorkingDataFolderPropertyLocation.IsDefined ||
                String.IsNullOrEmpty(workingDataFolder))
            {
                workingDataFolder = projectConfigFolder + "/Working Data";
            }

            if (!System.IO.Directory.Exists(workingDataFolder))
            {
                try
                { System.IO.Directory.CreateDirectory(workingDataFolder); }
                catch
                {
                    errorList.Add(
                        message: Locale.Get("NaturalDocs.Engine", "Error.CantCreateWorkingDataFolder(name)", workingDataFolder),
                        property: "WorkingDataFolder"
                        );

                    success = false;
                }
            }

            if (success == false)
            {
                return(false);
            }


            // Load the previous configuration state.  Remember that not every value in ProjectConfig is stored in Project.nd.

            ProjectConfig previousConfig  = null;
            var           projectNDParser = new Project_nd();

            if (ReparseEverything == false && System.IO.File.Exists(workingDataFolder + "/Project.nd"))
            {
                if (!projectNDParser.Load(workingDataFolder + "/Project.nd", out previousConfig))
                {
                    previousConfig = null;
                }
            }


            // Merge output target numbers from Project.nd into the settings.  These are not stored in Project.txt because they're
            // pretty expendible.

            if (previousConfig != null)
            {
                foreach (var target in combinedConfig.OutputTargets)
                {
                    foreach (var previousTarget in previousConfig.OutputTargets)
                    {
                        if (target.IsSameTarget(previousTarget))
                        {
                            target.Number = previousTarget.Number;
                            break;
                        }
                    }
                }
            }


            // Target validation

            if (combinedConfig.InputTargets.Count < 1)
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.NoInputTargets"),
                    property: "InputTargets"
                    );
                success = false;
            }
            if (combinedConfig.OutputTargets.Count < 1)
            {
                errorList.Add(
                    message: Locale.Get("NaturalDocs.Engine", "Error.NoOutputTargets"),
                    property: "OutputTargets"
                    );
                success = false;
            }

            for (int i = 0; i < combinedConfig.InputTargets.Count; i++)
            {
                if (combinedConfig.InputTargets[i].Validate(errorList, i) == false)
                {
                    success = false;
                }
            }
            for (int i = 0; i < combinedConfig.FilterTargets.Count; i++)
            {
                if (combinedConfig.FilterTargets[i].Validate(errorList, i) == false)
                {
                    success = false;
                }
            }
            for (int i = 0; i < combinedConfig.OutputTargets.Count; i++)
            {
                if (combinedConfig.OutputTargets[i].Validate(errorList, i) == false)
                {
                    success = false;
                }
            }

            if (success == false)
            {
                return(false);
            }


            // Determine the target numbers that are already used and reset duplicates.

            IDObjects.NumberSet usedSourceNumbers = new IDObjects.NumberSet();
            IDObjects.NumberSet usedImageNumbers  = new IDObjects.NumberSet();

            foreach (var target in combinedConfig.InputTargets)
            {
                if (target.Number != 0)
                {
                    if (target.Type == Files.InputType.Source)
                    {
                        if (usedSourceNumbers.Contains(target.Number))
                        {
                            target.Number = 0;
                            target.NumberPropertyLocation = Source.NotDefined;
                        }
                        else
                        {
                            usedSourceNumbers.Add(target.Number);
                        }
                    }

                    else if (target.Type == Files.InputType.Image)
                    {
                        if (usedImageNumbers.Contains(target.Number))
                        {
                            target.Number = 0;
                            target.NumberPropertyLocation = Source.NotDefined;
                        }
                        else
                        {
                            usedImageNumbers.Add(target.Number);
                        }
                    }
                }
            }


            IDObjects.NumberSet usedOutputNumbers    = new IDObjects.NumberSet();
            IDObjects.NumberSet outputNumbersToPurge = new IDObjects.NumberSet();

            foreach (var target in combinedConfig.OutputTargets)
            {
                if (target.Number != 0)
                {
                    if (usedOutputNumbers.Contains(target.Number))
                    {
                        target.Number = 0;
                        target.NumberPropertyLocation = Source.NotDefined;

                        // Since we don't know which of the two entries generated working data under this number, purge it to be safe.
                        outputNumbersToPurge.Add(target.Number);
                    }
                    else
                    {
                        usedOutputNumbers.Add(target.Number);
                    }
                }
            }


            // Assign numbers to the entries that don't already have them and generate default input folder names.

            foreach (var target in combinedConfig.InputTargets)
            {
                if (target.Type == Files.InputType.Source)
                {
                    if (target.Number == 0)
                    {
                        target.Number = usedSourceNumbers.LowestAvailable;
                        target.NumberPropertyLocation = Source.SystemGenerated;

                        usedSourceNumbers.Add(target.Number);
                    }

                    if (target.Name == null && combinedConfig.InputTargets.Count > 1)
                    {
                        target.GenerateDefaultName();
                    }
                }

                else if (target.Type == Files.InputType.Image)
                {
                    if (target.Number == 0)
                    {
                        target.Number = usedImageNumbers.LowestAvailable;
                        target.NumberPropertyLocation = Source.SystemGenerated;

                        usedImageNumbers.Add(target.Number);
                    }
                }
            }

            foreach (var target in combinedConfig.OutputTargets)
            {
                if (target.Number == 0)
                {
                    target.Number = usedOutputNumbers.LowestAvailable;
                    target.NumberPropertyLocation = Source.SystemGenerated;

                    usedOutputNumbers.Add(target.Number);

                    // If we're assigning it for the first time, purge it on the off chance that there's data left over from another
                    // builder.
                    outputNumbersToPurge.Add(target.Number);
                }
            }


            // Rebuild everything if there's an output target that didn't exist on the last run.

            foreach (var target in combinedConfig.OutputTargets)
            {
                bool foundMatch = false;

                if (previousConfig != null)
                {
                    foreach (var previousTarget in previousConfig.OutputTargets)
                    {
                        if (previousTarget.IsSameTarget(target))
                        {
                            foundMatch = true;
                            break;
                        }
                    }
                }

                if (foundMatch == false)
                {
                    RebuildAllOutput = true;
                    break;
                }
            }


            // Apply global settings.

            // DEPENDENCY: We assume all these settings are set in systemDefaultConfig so we don't have to worry about them being
            // undefined.
            tabWidth       = combinedConfig.TabWidth;
            documentedOnly = combinedConfig.DocumentedOnly;
            autoGroup      = combinedConfig.AutoGroup;
            shrinkFiles    = combinedConfig.ShrinkFiles;

            if (previousConfig == null ||
                tabWidth != previousConfig.TabWidth ||
                documentedOnly != previousConfig.DocumentedOnly ||
                autoGroup != previousConfig.AutoGroup)
            {
                ReparseEverything = true;
            }
            else if (previousConfig != null &&
                     shrinkFiles != previousConfig.ShrinkFiles)
            {
                RebuildAllOutput = true;
            }


            // Resave the configuration. Project_txt will handle skipping all the default and command line properties.

            projectTxtParser.Save(projectConfigFolder + "/Project.txt", combinedConfig, errorList);
            projectNDParser.Save(workingDataFolder + "/Project.nd", combinedConfig);


            // Create file sources and filters for Files.Manager

            foreach (var target in combinedConfig.InputTargets)
            {
                EngineInstance.Files.AddFileSource(CreateFileSource(target));
            }

            foreach (var target in combinedConfig.FilterTargets)
            {
                EngineInstance.Files.AddFilter(CreateFilter(target));
            }

            // Some people may put the output folder in their source folder.  Exclude it automatically.
            foreach (var target in combinedConfig.OutputTargets)
            {
                var filter = CreateOutputFilter(target);

                if (filter != null)
                {
                    EngineInstance.Files.AddFilter(filter);
                }
            }


            // Create more default filters

            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolder(ProjectConfigFolder));
            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolder(WorkingDataFolder));
            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolder(SystemConfigFolder));
            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolder(SystemStyleFolder));

            EngineInstance.Files.AddFilter(new Engine.Files.Filters.IgnoredSourceFolderRegex(new Regex.Config.DefaultIgnoredSourceFolderRegex()));


            // Check all input folder entries against the filters.

            for (int i = 0; i < combinedConfig.InputTargets.Count; i++)
            {
                if (combinedConfig.InputTargets[i] is Targets.SourceFolder)
                {
                    var sourceFolderTarget = (Targets.SourceFolder)combinedConfig.InputTargets[i];

                    if (sourceFolderTarget.Type == Files.InputType.Source &&
                        EngineInstance.Files.SourceFolderIsIgnored(sourceFolderTarget.Folder))
                    {
                        errorList.Add(
                            message: Locale.Get("NaturalDocs.Engine", "Error.SourceFolderIsIgnored(sourceFolder)", sourceFolderTarget.Folder),
                            propertyLocation: sourceFolderTarget.FolderPropertyLocation,
                            property: "InputTargets[" + i + "].Folder"
                            );

                        success = false;
                    }
                }
                else
                {
                    throw new NotImplementedException();
                }
            }


            // Create builders for Output.Manager

            foreach (var target in combinedConfig.OutputTargets)
            {
                // Merge the global project info so it has a complete configuration.  The configuration files have already been saved without it.
                MergeProjectInfo(target.ProjectInfo, combinedConfig.ProjectInfo);

                EngineInstance.Output.AddBuilder(CreateBuilder(target));
            }


            // Purge stray output working data, since otherwise it will be left behind if an output entry is removed.

            Regex.Config.OutputPathNumber outputPathNumberRegex = new Regex.Config.OutputPathNumber();
            bool raisedPossiblyLongOperationEvent = false;

            string[] outputDataFolders = System.IO.Directory.GetDirectories(workingDataFolder, "Output*", System.IO.SearchOption.TopDirectoryOnly);
            foreach (string outputDataFolder in outputDataFolders)
            {
                System.Text.RegularExpressions.Match match = outputPathNumberRegex.Match(outputDataFolder);
                if (match.Success)
                {
                    string numberString = match.Groups[1].ToString();
                    int    number;

                    if (String.IsNullOrEmpty(numberString))
                    {
                        number = 1;
                    }
                    else
                    {
                        number = int.Parse(numberString);
                    }

                    if (outputNumbersToPurge.Contains(number) || !usedOutputNumbers.Contains(number))
                    {
                        // Since we're deleting an entire folder, mark it as a possibly long operation.  Some output formats may create many
                        // files in there which could take a while to clear out.
                        if (!raisedPossiblyLongOperationEvent)
                        {
                            EngineInstance.StartPossiblyLongOperation("PurgingOutputWorkingData");
                            raisedPossiblyLongOperationEvent = true;
                        }

                        try
                        { System.IO.Directory.Delete(outputDataFolder, true); }
                        catch (Exception e)
                        {
                            if (!(e is System.IO.IOException || e is System.IO.DirectoryNotFoundException))
                            {
                                throw;
                            }
                        }
                    }
                }
            }

            string[] outputDataFiles = System.IO.Directory.GetFiles(workingDataFolder, "Output*.nd", System.IO.SearchOption.TopDirectoryOnly);
            foreach (string outputDataFile in outputDataFiles)
            {
                System.Text.RegularExpressions.Match match = outputPathNumberRegex.Match(outputDataFile);
                if (match.Success)
                {
                    string numberString = match.Groups[1].ToString();
                    int    number;

                    if (String.IsNullOrEmpty(numberString))
                    {
                        number = 1;
                    }
                    else
                    {
                        number = int.Parse(numberString);
                    }

                    if (outputNumbersToPurge.Contains(number) || !usedOutputNumbers.Contains(number))
                    {
                        // Since this should just be a few individual files we don't have to worry about it being a possibly long operation,
                        // although this will piggyback on that event if it was already raised.

                        System.IO.File.Delete(outputDataFile);
                    }
                }
            }

            if (raisedPossiblyLongOperationEvent)
            {
                EngineInstance.EndPossiblyLongOperation();
            }


            return(success);
        }
Ejemplo n.º 3
0
        /* Function: HandleException
         */
        static private void HandleException(Exception e)
        {
            var errorOutput = Console.Error;

            errorOutput.Write("\n\n------------------------------------------------------------\n");
            errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.CLI", "Crash.Exception",
                                                 "Natural Docs has closed because of the following error:"));
            errorOutput.WriteLine();


            // If it's a user friendly exception, just display it

            if (e.GetType() == typeof(Engine.Exceptions.UserFriendly) ||
                (e.GetType() == typeof(Engine.Exceptions.Thread) &&
                 e.InnerException.GetType() == typeof(Engine.Exceptions.UserFriendly)))
            {
                errorOutput.WriteLine(e.Message);
            }

            else
            {
                Path crashFile = EngineInstance.BuildCrashReport(e);


                // If we were able to build a crash report, display the exception and the report's location

                if (crashFile != null)
                {
                    errorOutput.WriteLine(e.Message);
                    errorOutput.WriteLine("(" + e.GetType() + ")");
                    errorOutput.WriteLine();
                    errorOutput.Write(Locale.SafeGet("NaturalDocs.CLI", "Crash.ReportAt(file).multiline",
                                                     "A crash report has been generated at {0}.\n" +
                                                     "Please include this file when asking for help at naturaldocs.org.\n", crashFile));
                }


                // If we couldn't build the crash report, display the information on the screen.

                else
                {
                    errorOutput.Write(EngineInstance.GetCrashInformation(e));
                    errorOutput.WriteLine();
                    errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.CLI", "Crash.IncludeInfoAndGetHelp",
                                                         "Please include this information when asking for help at naturaldocs.org."));
                }


                // Include a notice for outdated Mono versions

                if (Engine.SystemInfo.MonoVersionTooOld)
                {
                    errorOutput.WriteLine();
                    errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.Engine", "CrashReport.OutdatedMono(currentVersion, minimumVersion)",
                                                         "You appear to be using Mono {0}, which is very outdated.  This has been known to cause Natural Docs to crash.  Please update it to version {1} or higher.",
                                                         Engine.SystemInfo.MonoVersion, Engine.SystemInfo.MinimumMonoVersion));
                }
            }

            errorOutput.Write("------------------------------------------------------------\n\n");
        }
Ejemplo n.º 4
0
        /* Function: Start_Stage2
         *
         * Finishes loading and and combing the two versions of <Languages.txt>, returning whether it was successful.  If there
         * were any errors they will be added to errorList.
         *
         * This must be called after <Start_Stage1()> has been called, and also <CommentTypes.Manager.Start_Stage1()>.  This
         * finalizes any settings which also depend on <Comments.txt>.
         *
         * Dependencies:
         *
         *		- <Config.Manager> must be started before this class can start.
         *		- <Start_Stage1()> must be called and return true before this function can be called.
         *		- <CommentTypes.Manager.Start_Stage1()> must be called and return true before this function can be called.
         */
        public bool Start_Stage2(Errors.ErrorList errorList)
        {
            bool success = true;


            // Go through the lists and apply prototype enders, since CommentTypes.Manager should have all their names now.

            if (!MergePrototypeEndersInto_Stage2(ref config, systemTextConfig, errorList))
            {
                success = false;
            }
            if (!MergePrototypeEndersInto_Stage2(ref config, projectTextConfig, errorList))
            {
                success = false;
            }

            if (!success)
            {
                return(false);
            }


            // Now we have our final configuration and everything is okay.  Save the text files again to reformat them.

            TouchUp_Stage2(ref systemTextConfig, config);
            TouchUp_Stage2(ref projectTextConfig, config);

            Path systemTextConfigPath  = EngineInstance.Config.SystemConfigFolder + "/Languages.txt";
            Path projectTextConfigPath = EngineInstance.Config.ProjectConfigFolder + "/Languages.txt";

            ConfigFiles.TextFileParser textConfigFileParser = new ConfigFiles.TextFileParser();

            // If the project Languages.txt didn't exist, saving the blank structure that was created will create a default one.
            if (!textConfigFileParser.Save(projectTextConfigPath, PropertySource.ProjectLanguagesFile, projectTextConfig, errorList))
            {
                return(false);
            }
            ;

            // We don't care if we're not able to resave the system Languages.txt since it may be in a protected location.
            textConfigFileParser.Save(systemTextConfigPath, PropertySource.SystemLanguagesFile, systemTextConfig, errorList: null);


            // Save Languages.nd as well.

            Path lastRunConfigPath = EngineInstance.Config.WorkingDataFolder + "/Languages.nd";

            ConfigFiles.BinaryFileParser binaryConfigFileParser = new ConfigFiles.BinaryFileParser();

            binaryConfigFileParser.Save(lastRunConfigPath, config);


            // Compare the config against the previous one and reparse everything if there are changes.  Changes that would invalidate
            // IDs have already been handled.

            if (config != lastRunConfig)
            {
                EngineInstance.AddStartupIssues(StartupIssues.NeedToReparseAllFiles);
            }


            // We're done with these variables which were only needed between start stages 1 and 2.

            systemTextConfig  = null;
            projectTextConfig = null;
            mergedTextConfig  = null;
            lastRunConfig     = null;

            started = true;
            return(true);
        }
Ejemplo n.º 5
0
        public override bool Start(Errors.ErrorList errorList)
        {
            int           errors           = errorList.Count;
            StartupIssues newStartupIssues = StartupIssues.None;


            //
            // Validate the output folder.
            //

            if (System.IO.Directory.Exists(config.Folder) == false)
            {
                errorList.Add(Locale.Get("NaturalDocs.Engine", "Error.FolderDoesntExist(type, name)", "output", config.Folder));
                return(false);
            }


            //
            // Load and validate the styles, including any inherited styles.
            //

            string    styleName   = config.ProjectInfo.StyleName;
            Style_txt styleParser = new Style_txt();

            if (styleName == null)
            {
                style = EngineInstance.Styles.LoadStyle("Default", errorList, Config.PropertySource.SystemDefault);
            }

            else if (EngineInstance.Styles.StyleExists(styleName))
            {
                style = EngineInstance.Styles.LoadStyle(styleName, errorList, config.ProjectInfo.StyleNamePropertyLocation);
            }

            // Check if it's an empty folder we want to generate a default Style.txt for
            else if (System.IO.Directory.Exists(EngineInstance.Config.ProjectConfigFolder + '/' + styleName) &&
                     !System.IO.File.Exists(EngineInstance.Config.ProjectConfigFolder + '/' + styleName + "/Style.txt"))
            {
                style = new Styles.Advanced(EngineInstance.Config.ProjectConfigFolder + '/' + styleName + "/Style.txt");

                // Inherit Default so everything still works before it's filled out.
                style.AddInheritedStyle("Default", Config.PropertySource.SystemGenerated);

                if (!styleParser.Save((Styles.Advanced)style, errorList, false))
                {
                    return(false);
                }

                // Now we have to reload it so it loads the inherited style as well.
                style = EngineInstance.Styles.LoadStyle(styleName, errorList, config.ProjectInfo.StyleNamePropertyLocation);
            }

            else
            {
                errorList.Add(Locale.Get("NaturalDocs.Engine", "Style.txt.CantFindStyle(name)", styleName),
                              config.ProjectInfo.StyleNamePropertyLocation);
                return(false);
            }

            stylesWithInheritance = style.BuildInheritanceList();


            //
            // Load Config.nd
            //

            Config_nd             binaryConfigParser = new Config_nd();
            List <Style>          previousStyles;
            List <FileSourceInfo> previousFileSourceInfoList;
            bool hasBinaryConfigFile = false;

            if (!EngineInstance.HasIssues(StartupIssues.NeedToStartFresh))
            {
                hasBinaryConfigFile = binaryConfigParser.Load(WorkingDataFolder + "/Config.nd", out previousStyles, out previousFileSourceInfoList);
            }
            else             // start fresh
            {
                previousStyles             = new List <Style>();
                previousFileSourceInfoList = new List <FileSourceInfo>();
            }


            //
            // Compare to the previous list of styles.
            //

            bool inPurgingOperation = false;

            if (!hasBinaryConfigFile)
            {
                // If we don't have the binary config file we have to purge every style folder because some of them may no longer be in
                // use and we won't know which.
                PurgeAllStyleFolders(ref inPurgingOperation);

                newStartupIssues |= StartupIssues.NeedToReparseStyleFiles;
            }

            else             // (hasBinaryConfigFile)
            {
                // Purge the style folders for any no longer in use.

                foreach (var previousStyle in previousStyles)
                {
                    bool stillExists = false;

                    foreach (var currentStyle in stylesWithInheritance)
                    {
                        if (currentStyle.IsSameFundamentalStyle(previousStyle))
                        {
                            stillExists = true;
                            break;
                        }
                    }

                    if (!stillExists)
                    {
                        PurgeStyleFolder(previousStyle.Name, ref inPurgingOperation);
                    }
                }

                // Reparse styles on anything new.  If a style is new we can't assume all its files are going to be sent to the
                // IChangeWatcher functions because another output target may have been using it, and thus they are already in
                // Files.Manager.

                foreach (var currentStyle in stylesWithInheritance)
                {
                    bool foundMatch = false;

                    foreach (var previousStyle in previousStyles)
                    {
                        if (previousStyle.IsSameFundamentalStyle(currentStyle))
                        {
                            foundMatch = true;
                            break;
                        }
                    }

                    if (!foundMatch)
                    {
                        newStartupIssues |= StartupIssues.NeedToReparseStyleFiles;
                        break;
                    }
                }
            }


            //
            // Compare to the previous list of FileSources.
            //

            if (!hasBinaryConfigFile)
            {
                // If we don't have the binary config file we need to rebuild all the output because we don't know which FileSource was
                // previously set to which number, which determines which output folder they use, like /files vs /files2.
                newStartupIssues |= StartupIssues.NeedToRebuildAllOutput;

                // This also means we have to purge every source or image output folder because we won't know which changed or are
                // no longer in use.
                PurgeAllSourceAndImageFolders(ref inPurgingOperation);
            }

            else              // (hasBinaryConfigFile)
            {
                bool hasDeletions = false;
                bool hasAdditions = false;


                // Purge the output folders of any deleted FileSources

                foreach (var previousFileSourceInfo in previousFileSourceInfoList)
                {
                    bool stillExists = false;

                    foreach (var fileSource in EngineInstance.Files.FileSources)
                    {
                        if (previousFileSourceInfo.IsSameFundamentalFileSource(fileSource))
                        {
                            stillExists = true;
                            break;
                        }
                    }

                    if (!stillExists)
                    {
                        hasDeletions = true;
                        Path outputFolder;

                        if (previousFileSourceInfo.Type == InputType.Source)
                        {
                            outputFolder = Paths.SourceFile.OutputFolder(OutputFolder, previousFileSourceInfo.Number);
                        }
                        else if (previousFileSourceInfo.Type == InputType.Image)
                        {
                            outputFolder = Paths.Image.OutputFolder(OutputFolder, previousFileSourceInfo.Number, previousFileSourceInfo.Type);
                        }
                        else
                        {
                            throw new NotImplementedException();
                        }

                        PurgeFolder(outputFolder, ref inPurgingOperation);
                    }
                }


                // Check if any FileSources were added

                foreach (var fileSource in EngineInstance.Files.FileSources)
                {
                    if (fileSource.Type == InputType.Source || fileSource.Type == InputType.Image)
                    {
                        bool foundMatch = false;

                        foreach (var previousFileSourceInfo in previousFileSourceInfoList)
                        {
                            if (previousFileSourceInfo.IsSameFundamentalFileSource(fileSource))
                            {
                                foundMatch = true;
                                break;
                            }
                        }

                        if (!foundMatch)
                        {
                            hasAdditions = true;
                            break;
                        }
                    }
                }


                // If there were both additions and deletions, force a rebuild.  This covers if a FileSource was simply moved from one
                // number to another, in which case the rebuild is required to populate the new folder.  This also covers if a folder
                // FileSource is replaced by one for its parent folder, in which case a rebuild is required to recreate the output for the
                // files in the child folder.

                if (hasAdditions && hasDeletions)
                {
                    newStartupIssues |= StartupIssues.NeedToRebuildAllOutput;
                }
            }


            //
            // Load BuildState.nd
            //

            BuildState_nd buildStateParser        = new BuildState_nd();
            bool          hasBinaryBuildStateFile = false;

            if (!EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                          StartupIssues.FileIDsInvalidated |
                                          StartupIssues.CodeIDsInvalidated |
                                          StartupIssues.CommentIDsInvalidated))
            {
                hasBinaryBuildStateFile = buildStateParser.Load(WorkingDataFolder + "/BuildState.nd", out buildState, out unprocessedChanges);
            }
            else             // start fresh
            {
                buildState         = new BuildState();
                unprocessedChanges = new UnprocessedChanges();
            }

            if (!hasBinaryBuildStateFile)
            {
                // If we don't have a build state file we need to reparse all the source files because we need to know sourceFilesWithContent
                // and classesWithContent.  We also need to rebuild all the output because we don't know if there was anything left in
                // sourceFilesToRebuild from the last run.  But those two flags actually aren't enough, because it will reparse those files, send
                // them to CodeDB, and then CodeDB won't send topic updates if the underlying content hasn't changed.  We'd actually need
                // to add all files and classes to UnprocessedChanges, but the Files module isn't started yet.  So f**k it, blow it all up and start
                // over.
                newStartupIssues |= StartupIssues.NeedToStartFresh;

                // Purge everything so no stray files are left behind from the previous build.
                PurgeAllSourceAndImageFolders(ref inPurgingOperation);
                PurgeAllClassFolders(ref inPurgingOperation);
                PurgeAllDatabaseFolders(ref inPurgingOperation);
                PurgeAllStyleFolders(ref inPurgingOperation);
                PurgeAllMenuFolders(ref inPurgingOperation);
                PurgeAllSearchIndexFolders(ref inPurgingOperation);
            }


            //
            // We're done with anything that could purge.
            //

            FinishedPurging(ref inPurgingOperation);


            //
            // Resave the Style.txt-based styles.
            //

            foreach (var style in stylesWithInheritance)
            {
                if (style is Styles.Advanced)
                {
                    var  advancedStyle = style as Styles.Advanced;
                    bool isSystemStyle = EngineInstance.Config.SystemStyleFolder.Contains(advancedStyle.ConfigFile);

                    // No error on save for system styles.
                    styleParser.Save(advancedStyle, errorList, noErrorOnFail: isSystemStyle);
                }
            }


            //
            // Save Config.nd.
            //

            if (!System.IO.Directory.Exists(WorkingDataFolder))
            {
                System.IO.Directory.CreateDirectory(WorkingDataFolder);
            }

            List <FileSourceInfo> fileSourceInfoList = new List <FileSourceInfo>();

            foreach (var fileSource in EngineInstance.Files.FileSources)
            {
                if (fileSource.Type == Files.InputType.Source || fileSource.Type == Files.InputType.Image)
                {
                    FileSourceInfo fileSourceInfo = new FileSourceInfo();
                    fileSourceInfo.CopyFrom(fileSource);
                    fileSourceInfoList.Add(fileSourceInfo);
                }
                ;
            }

            binaryConfigParser.Save(WorkingDataFolder + "/Config.nd", stylesWithInheritance, fileSourceInfoList);


            //
            // Always rebuild the scaffolding since they're quick.  If you ever make this differential, remember that FramePage depends
            // on the project name and other information.
            //

            unprocessedChanges.AddFramePage();
            unprocessedChanges.AddMainStyleFiles();


            //
            // Load up unprocessedChanges if we're rebuilding
            //

            if (EngineInstance.HasIssues(StartupIssues.NeedToRebuildAllOutput) ||
                (newStartupIssues & StartupIssues.NeedToRebuildAllOutput) != 0)
            {
                unprocessedChanges.AddSourceFiles(buildState.sourceFilesWithContent);
                unprocessedChanges.AddClasses(buildState.classesWithContent);
                unprocessedChanges.AddImageFiles(buildState.usedImageFiles);

                newStartupIssues |= StartupIssues.NeedToReparseStyleFiles;

                unprocessedChanges.AddMainStyleFiles();
                unprocessedChanges.AddMainSearchFiles();
                unprocessedChanges.AddFramePage();
                unprocessedChanges.AddMenu();

                // We'll handle search prefixes after starting SearchIndex
            }


            //
            // Create the search index, watch other modules, and apply new StartupIssues
            //

            searchIndex = new SearchIndex.Manager(this);

            EngineInstance.CodeDB.AddChangeWatcher(this);
            EngineInstance.Files.AddChangeWatcher(this);
            searchIndex.AddChangeWatcher(this);
            EngineInstance.AddStartupWatcher(this);

            if (newStartupIssues != StartupIssues.None)
            {
                EngineInstance.AddStartupIssues(newStartupIssues, dontNotify: this);
            }

            searchIndex.Start(errorList);


            //
            // If we're rebuilding everything, add the search index prefixes now that that's started
            //

            if (EngineInstance.HasIssues(StartupIssues.NeedToRebuildAllOutput))
            {
                var usedPrefixes = searchIndex.UsedPrefixes();

                foreach (var searchPrefix in usedPrefixes)
                {
                    unprocessedChanges.AddSearchPrefix(searchPrefix);
                }
            }


            return(errors == errorList.Count);
        }
Ejemplo n.º 6
0
        public void AttachScript(string path, string typeName)
        {
            var compiler = EngineInstance.GetCompilerService();

            EngineInstance.AttachedScriptsFactory.AttachByPath(compiler, path, typeName);
        }
Ejemplo n.º 7
0
        /* Function: Start
         *
         * Loads and combines the two versions of <Languages.txt>, returning whether it was successful.  If there were any errors
         * they will be added to errorList.
         *
         * Dependencies:
         *
         *		- <Config.Manager> and <CommentTypes.Manager> must be started before using the rest of the class.
         */
        public bool Start(Errors.ErrorList errorList)
        {
            StartupIssues newStartupIssues = StartupIssues.None;

            List <ConfigFileLanguage> systemLanguageList;
            List <ConfigFileLanguage> projectLanguageList;
            List <string>             ignoredSystemExtensions;
            List <string>             ignoredProjectExtensions;

            List <Language> binaryLanguages;
            List <KeyValuePair <string, int> > binaryAliases;
            List <KeyValuePair <string, int> > binaryExtensions;
            List <KeyValuePair <string, int> > binaryShebangStrings;
            List <string> binaryIgnoredExtensions;

            // The return value, which is whether we were able to successfully load and parse the system Languages.txt, and if
            // it exists, the project Languages.txt.  The project Languages.txt not existing is not a failure.
            bool success = true;


            // First add all the predefined languages, since they may be subclassed.

            foreach (Language language in predefinedLanguages)
            {
                languages.Add(language);
            }


            // We need the ID numbers to stay consistent between runs, so we create all the languages from the binary file
            // next.  We'll worry about comparing their attributes with the text files and seeing if any were added or deleted later.

            Languages_nd languagesNDParser = new Languages_nd(this);

            // Don't bother going through the effort if we're rebuilding everything anyway.
            if (EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                         StartupIssues.CodeIDsInvalidated))
            {
                binaryLanguages         = new List <Language>();
                binaryAliases           = new List <KeyValuePair <string, int> >();
                binaryExtensions        = new List <KeyValuePair <string, int> >();
                binaryShebangStrings    = new List <KeyValuePair <string, int> >();
                binaryIgnoredExtensions = new List <string>();

                newStartupIssues |= StartupIssues.NeedToReparseAllFiles |
                                    StartupIssues.CodeIDsInvalidated;
            }

            else if (!languagesNDParser.Load(EngineInstance.Config.WorkingDataFolder + "/Languages.nd", out binaryLanguages,
                                             out binaryAliases, out binaryExtensions, out binaryShebangStrings, out binaryIgnoredExtensions))
            {
                newStartupIssues |= StartupIssues.NeedToReparseAllFiles |
                                    StartupIssues.CodeIDsInvalidated;
                // Even though it failed, LoadBinaryFiles will still have created valid empty objects for them.
            }

            else             // LoadBinaryFile succeeded
            {
                // We use a try block so if anything screwy happens, like two languages having the same ID number and thus
                // causing an exception when added, we can continue as if the binary file didn't parse at all.
                try
                {
                    foreach (Language binaryLanguage in binaryLanguages)
                    {
                        // We don't add the binary language itself because we only want those for comparison purposes.  We otherwise
                        // want the languages to be at their default values because the Languages.txt versions will only set some
                        // attributes, not all.

                        // Check for predefined languages of the same name.  If any of the binary languages' IDs collide with the
                        // predefined languages' ones, it will be taken care of by the exception handler.
                        Language existingLanguage = languages[binaryLanguage.Name];

                        if (existingLanguage == null)
                        {
                            Language newLanguage = new Language(this, binaryLanguage.Name);
                            newLanguage.ID           = binaryLanguage.ID;
                            newLanguage.InBinaryFile = true;

                            languages.Add(newLanguage);
                        }
                        else
                        {
                            existingLanguage.InBinaryFile = true;
                        }
                    }
                }
                catch
                {
                    languages.Clear();

                    newStartupIssues |= StartupIssues.NeedToReparseAllFiles |
                                        StartupIssues.CodeIDsInvalidated;

                    foreach (Language predefinedLanguage in predefinedLanguages)
                    {
                        languages.Add(predefinedLanguage);
                    }

                    // Clear them since they may be used later in this function.
                    binaryLanguages.Clear();
                    binaryAliases.Clear();
                    binaryExtensions.Clear();
                    binaryShebangStrings.Clear();
                    binaryIgnoredExtensions.Clear();

                    // Otherwise ignore the exception and continue.
                }
            }


            Path systemFile  = EngineInstance.Config.SystemConfigFolder + "/Languages.txt";
            Path projectFile = EngineInstance.Config.ProjectConfigFolder + "/Languages.txt";

            Languages_txt languagesTxtParser = new Languages_txt();


            // Load the files.

            if (!languagesTxtParser.Load(systemFile, Config.PropertySource.SystemLanguageFile,
                                         out systemLanguageList, out ignoredSystemExtensions, errorList))
            {
                success = false;
                // Continue anyway because we want to show errors from both files.
            }

            if (System.IO.File.Exists(projectFile))
            {
                if (!languagesTxtParser.Load(projectFile, Config.PropertySource.ProjectLanguageFile,
                                             out projectLanguageList, out ignoredProjectExtensions, errorList))
                {
                    success = false;
                }
            }
            else
            {
                // The project file not existing is not an error condition.  Fill in the variables with empty structures.
                projectLanguageList      = new List <ConfigFileLanguage>();
                ignoredProjectExtensions = new List <string>();
            }

            if (success == false)
            {
                return(false);
            }


            // Combine the ignored extensions.

            StringSet ignoredExtensions = new StringSet(KeySettingsForExtensions);

            foreach (string extension in ignoredSystemExtensions)
            {
                ignoredExtensions.Add(extension);
            }

            foreach (string extension in ignoredProjectExtensions)
            {
                ignoredExtensions.Add(extension);
            }


            // Add the languages.  We don't need to do separate passes for standard entries and alter entries because alter
            // entries should only appear in the project file and only apply to types in the system file.  Anything else is either an
            // error (system file can't alter a project entry) or would have been simplified out by LoadFile (a file with an alter
            // entry applying to a language in the same file.)  Start_AddLanguage() also prevents inappropriate properties from
            // being set on languages, like Line Comment on one with full language support.

            foreach (ConfigFileLanguage configFileLanguage in systemLanguageList)
            {
                if (!Start_AddLanguage(configFileLanguage, systemFile, true, ignoredExtensions, errorList))
                {
                    success = false;
                }
            }

            foreach (ConfigFileLanguage configFileLanguage in projectLanguageList)
            {
                if (!Start_AddLanguage(configFileLanguage, projectFile, false, ignoredExtensions, errorList))
                {
                    success = false;
                }
            }

            if (success == false)
            {
                return(false);
            }


            // Now that everything's in languages we can delete the ones that weren't in the config files, such as predefined
            // languages that were removed or languages that were in the binary file from the last run but were deleted.  We
            // have to put them on a list and delete them in a second pass because deleting them while iterating through would
            // screw up the iterator.

            List <string> deletedLanguageNames = new List <string>();

            foreach (Language language in languages)
            {
                if (language.InConfigFiles == false)
                {
                    deletedLanguageNames.Add(language.Name);

                    // Check this flag so we don't set it to changed if we're deleting a predefined language that wasn't in the binary
                    // file.
                    if (language.InBinaryFile == true)
                    {
                        newStartupIssues |= StartupIssues.NeedToReparseAllFiles;
                    }
                }
            }

            foreach (string deletedLanguageName in deletedLanguageNames)
            {
                languages.Remove(deletedLanguageName);
            }


            // Everything is okay at this point.  Save the files again to reformat them.  If the project file didn't exist, saving it
            // with the empty structures will create it.

            Start_FixCapitalization(systemLanguageList);
            Start_FixCapitalization(projectLanguageList);

            if (!languagesTxtParser.Save(projectFile, projectLanguageList, ignoredProjectExtensions, errorList, true, false))
            {
                success = false;
            }
            ;

            if (!languagesTxtParser.Save(systemFile, systemLanguageList, ignoredSystemExtensions, errorList, false, true))
            {
                success = false;
            }
            ;


            // Generate alternate comment styles.  We don't want these included in the config files but we do want them in the
            // binary files in case the generation method changes in a future version.

            foreach (Language language in languages)
            {
                if (language.Type == Language.LanguageType.BasicSupport)
                {
                    language.GenerateJavadocCommentStrings();
                    language.GenerateXMLCommentStrings();
                }
            }


            // Compare the structures with the binary ones to see if anything changed.

            if (binaryLanguages.Count != languages.Count ||
                binaryAliases.Count != aliases.Count ||
                binaryExtensions.Count != extensions.Count ||
                binaryShebangStrings.Count != shebangStrings.Count ||
                binaryIgnoredExtensions.Count != ignoredExtensions.Count)
            {
                newStartupIssues |= StartupIssues.NeedToReparseAllFiles;
            }

            // Only bother to do a detailed comparison if we're not already reparsing everything.
            else if (!EngineInstance.HasIssues(StartupIssues.NeedToReparseAllFiles) &&
                     (newStartupIssues & StartupIssues.NeedToReparseAllFiles) == 0)
            {
                bool changed = false;

                foreach (Language binaryLanguage in binaryLanguages)
                {
                    Language language = languages[binaryLanguage.Name];

                    if (language == null || binaryLanguage != language)
                    {
                        changed = true;
                        break;
                    }
                }

                if (changed == false)
                {
                    foreach (string binaryIgnoredExtension in binaryIgnoredExtensions)
                    {
                        if (ignoredExtensions.Contains(binaryIgnoredExtension) == false)
                        {
                            changed = true;
                            break;
                        }
                    }
                }

                if (changed == false)
                {
                    foreach (KeyValuePair <string, int> binaryAliasPair in binaryAliases)
                    {
                        // We can use ID instead of Name because we know they match now.
                        if (aliases.ContainsKey(binaryAliasPair.Key) == false ||
                            aliases[binaryAliasPair.Key].ID != binaryAliasPair.Value)
                        {
                            changed = true;
                            break;
                        }
                    }
                }

                if (changed == false)
                {
                    foreach (KeyValuePair <string, int> binaryExtensionPair in binaryExtensions)
                    {
                        // We can use ID instead of Name because we know they match now.
                        if (extensions.ContainsKey(binaryExtensionPair.Key) == false ||
                            extensions[binaryExtensionPair.Key].ID != binaryExtensionPair.Value)
                        {
                            changed = true;
                            break;
                        }
                    }
                }

                if (changed == false)
                {
                    foreach (KeyValuePair <string, int> binaryShebangStringPair in binaryShebangStrings)
                    {
                        // We can use ID instead of Name because we know they match now.
                        if (shebangStrings.ContainsKey(binaryShebangStringPair.Key) == false ||
                            shebangStrings[binaryShebangStringPair.Key].ID != binaryShebangStringPair.Value)
                        {
                            changed = true;
                            break;
                        }
                    }
                }

                if (changed)
                {
                    newStartupIssues |= StartupIssues.NeedToReparseAllFiles;
                }
            }


            languagesNDParser.Save(EngineInstance.Config.WorkingDataFolder + "/Languages.nd",
                                   languages, aliases, extensions, shebangStrings, ignoredExtensions);


            if (newStartupIssues != StartupIssues.None)
            {
                EngineInstance.AddStartupIssues(newStartupIssues);
            }

            return(success);
        }
Ejemplo n.º 8
0
        /* Function: AddAllFiles
         * Goes through all the files in the <FileSource> and calls <Files.Manager.AddOrUpdateFile()> on each one.
         */
        override public void AddAllFiles(CancelDelegate cancelDelegate)
        {
            status.Reset();
            bool forceReparse = EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                                         StartupIssues.NeedToReparseAllFiles);

            Path path = (FileSource as FileSources.SourceFolder).Path;

            // Using a string stack instead of Path stack because the I/O functions will return strings and there's no need to normalize
            // them all or otherwise use Path functions on them.
            Stack <string> foldersToSearch = new Stack <string>();

            foldersToSearch.Push(path);

            while (foldersToSearch.Count > 0)
            {
                string folder = foldersToSearch.Pop();

                if (Manager.SourceFolderIsIgnored(folder))
                {
                    continue;
                }

                status.AddFolders(InputType.Source, 1);

                string[] subfolders = System.IO.Directory.GetDirectories(folder);

                if (cancelDelegate())
                {
                    return;
                }

                foreach (string subfolder in subfolders)
                {
                    foldersToSearch.Push(subfolder);
                }

                string[] files = System.IO.Directory.GetFiles(folder);

                if (cancelDelegate())
                {
                    return;
                }

                foreach (string file in files)
                {
                    Path     filePath  = file;
                    string   extension = filePath.Extension;
                    FileType?fileType  = null;

                    if (EngineInstance.Languages.FromFileExtension(extension) != null)
                    {
                        fileType = FileType.Source;
                    }
                    // We also look for images in the source folders because "(see image.jpg)" may be relative to the source
                    // file instead of an image folder.
                    else if (Files.Manager.ImageExtensions.Contains(extension))
                    {
                        fileType = FileType.Image;
                    }

                    if (fileType != null)
                    {
                        status.AddFiles((FileType)fileType, 1);
                        Manager.AddOrUpdateFile(filePath, (FileType)fileType, System.IO.File.GetLastWriteTimeUtc(file), forceReparse);
                    }

                    if (cancelDelegate())
                    {
                        return;
                    }
                }
            }
        }
Ejemplo n.º 9
0
 /// <summary>
 /// The Cannon() function fires a missile heading a specified range and direction.
 /// Cannon() returns 1 (true) if a missile was fired, or 0 (false) if the cannon is reloading.
 /// Degree is forced into the range 0-359 as in Scan() and Drive().
 /// Range can be 0-700, with greater ranges truncated to 700.
 ///
 /// Examples:
 ///    degree = 45;                              // set a direction to test
 ///    if ((range=Scan(robot, degree, 2)) > 0)   // see if a target is there
 ///      Cannon(robot, degree, range);           // fire a missile
 /// </summary>
 /// <param name="robot"></param>
 /// <param name="degree"></param>
 /// <param name="range"></param>
 /// <returns></returns>
 public static bool Cannon(Robot robot, int degree, int range)
 {
     return(EngineInstance.FireCannon(robot, degree, range));
 }
Ejemplo n.º 10
0
        /* Function: AddAllFiles
         * Goes through all the files for all the loaded styles in <Styles.Manager> and calls <Files.Manager.AddOrUpdateFile()>
         * on each one.
         */
        override public void AddAllFiles(CancelDelegate cancelDelegate)
        {
            status.Reset();

            IList <Style> styles       = EngineInstance.Styles.LoadedStyles;
            bool          forceReparse = EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                                                  StartupIssues.NeedToReparseAllFiles |
                                                                  StartupIssues.NeedToReparseStyleFiles);

            // String stack instead of Path stack because the IO functions will return strings and there's no need to normalize
            // them all or otherwise use Path functions on them.
            Stack <string> foldersToSearch = new Stack <string>();

            foreach (var style in styles)
            {
                if (style is Styles.CSSOnly)
                {
                    Path cssFile = (style as Styles.CSSOnly).CSSFile;

                    status.AddFiles(Files.FileType.Style, 1);

                    EngineInstance.Files.AddOrUpdateFile(cssFile, Files.FileType.Style,
                                                         System.IO.File.GetLastWriteTimeUtc(cssFile), forceReparse);
                }

                else if (style is Styles.Advanced)
                {
                    foldersToSearch.Clear();
                    foldersToSearch.Push((style as Styles.Advanced).Folder);

                    status.AddFolders(Files.InputType.Style, 1);

                    while (foldersToSearch.Count > 0)
                    {
                        string folder = foldersToSearch.Pop();

                        string[] subfolders = System.IO.Directory.GetDirectories(folder);

                        status.AddFolders(Files.InputType.Style, subfolders.Length);

                        foreach (string subfolder in subfolders)
                        {
                            foldersToSearch.Push(subfolder);
                        }

                        if (cancelDelegate())
                        {
                            return;
                        }

                        string[] files = System.IO.Directory.GetFiles(folder);

                        foreach (string file in files)
                        {
                            if (style.Contains(file))
                            {
                                status.AddFiles(Files.FileType.Style, 1);

                                EngineInstance.Files.AddOrUpdateFile(file, Files.FileType.Style,
                                                                     System.IO.File.GetLastWriteTimeUtc(file), forceReparse);

                                if (cancelDelegate())
                                {
                                    return;
                                }
                            }
                        }
                    }
                }

                else
                {
                    throw new NotImplementedException();
                }
            }
        }
Ejemplo n.º 11
0
 /// <summary>
 /// The Scan() function invokes the robot's scanner, at a specified degree and resolution.
 /// Scan() returns 0 if no robots are within the scan range or a positive integer representing
 /// the  range to the closest robot.  Degree should be within the range 0-359, otherwise degree
 /// is forced into 0-359 by a modulo 360 operation, and made positive if necessary.
 /// Resolution controls the scanner's sensing resolution, up to +/- 10 degrees.
 ///
 /// Examples:
 ///   range = Scan(robot, 45, 0);   // scan 45, with no variance
 ///   range = Scan(robot, 365, 10); // scans the range from 355 to 15
 /// </summary>
 /// <param name="robot"></param>
 /// <param name="degree"></param>
 /// <param name="resolution"></param>
 /// <returns></returns>
 public static int Scan(Robot robot, int degree, int resolution)
 {
     return(EngineInstance.Scan(robot, degree, resolution));
 }
Ejemplo n.º 12
0
 /// <summary>
 /// Triggers the automation engine to stop based on a hooked key press
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 void OnHookStopped(object sender, EventArgs e)
 {
     uiBtnCancel_Click(null, null);
     GlobalHook.HookStopped -= new EventHandler(OnHookStopped);
     EngineInstance.CancelScript();
 }
Ejemplo n.º 13
0
        private static bool BuildDocumentation(ErrorList errorList)
        {
            ShowConsoleHeader();

            EngineInstance.AddStartupWatcher(new EngineStartupWatcher());


            executionTimer.Start("Engine Startup");

            if (EngineInstance.Start(errorList, commandLineConfig) == true)
            {
                executionTimer.End("Engine Startup");


                // File Search

                executionTimer.Start("Finding Source Files");

                var adderProcess = EngineInstance.Files.CreateAdderProcess();

                using (StatusManagers.FileSearch statusManager = new StatusManagers.FileSearch(adderProcess))
                {
                    statusManager.Start();

                    Multithread("File Adder", adderProcess.WorkOnAddingAllFiles);

                    statusManager.End();
                }

                EngineInstance.Files.DeleteFilesNotReAdded(Engine.Delegates.NeverCancel);
                adderProcess.Dispose();

                executionTimer.End("Finding Source Files");


                // Rebuild notice

                string alternativeStartMessage = null;

                if (EngineInstance.Config.UserWantsEverythingRebuilt ||
                    EngineInstance.Config.UserWantsOutputRebuilt)
                {
                    alternativeStartMessage = "Status.RebuildEverythingByRequest";
                }
                else if (EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                                  StartupIssues.NeedToReparseAllFiles |
                                                  StartupIssues.NeedToRebuildAllOutput))
                {
                    alternativeStartMessage = "Status.RebuildEverythingAutomatically";
                }


                // Parsing

                executionTimer.Start("Parsing Source Files");

                var changeProcessor = EngineInstance.Files.CreateChangeProcessor();

                using (StatusManagers.Parsing statusManager = new StatusManagers.Parsing(changeProcessor, alternativeStartMessage))
                {
                    statusManager.Start();
                    totalFileChanges = statusManager.TotalFilesToProcess;

                    Multithread("Parser", changeProcessor.WorkOnProcessingChanges);

                    statusManager.End();
                }

                changeProcessor.Dispose();

                executionTimer.End("Parsing Source Files");


                // Resolving

                executionTimer.Start("Resolving Links");

                var resolverProcess = EngineInstance.Links.CreateResolverProcess();

                using (StatusManagers.ResolvingLinks statusManager = new StatusManagers.ResolvingLinks(resolverProcess))
                {
                    statusManager.Start();

                    Multithread("Resolver", resolverProcess.WorkOnResolvingLinks);

                    statusManager.End();
                }

                resolverProcess.Dispose();

                executionTimer.End("Resolving Links");


                // Building

                executionTimer.Start("Building Output");

                var builderProcess = EngineInstance.Output.CreateBuilderProcess();

                using (StatusManagers.Building statusManager = new StatusManagers.Building(builderProcess))
                {
                    statusManager.Start();

                    Multithread("Builder", builderProcess.WorkOnUpdatingOutput);
                    Multithread("Finalizer", builderProcess.WorkOnFinalizingOutput);

                    statusManager.End();
                }

                builderProcess.Dispose();

                executionTimer.End("Building Output");


                // End

                EngineInstance.Cleanup(Delegates.NeverCancel);

                ShowConsoleFooter(true);
                return(true);
            }

            else             // engine did not start correctly
            {
                executionTimer.End("Engine Startup");

                ShowConsoleFooter(false);
                return(false);
            }
        }
Ejemplo n.º 14
0
        /* Function: Start_Stage2
         *
         * Finishes loading and and combing the two versions of <Comments.txt>, returning whether it was successful.  If there
         * were any errors they will be added to errorList.
         *
         * This must be called after <Start_Stage1()> has been called, and also <Languages.Manager.Start_Stage1()>.  This
         * finalizes any settings which also depend on <Languages.txt>.
         *
         * Dependencies:
         *
         *		- <Config.Manager> must be started before this class can start.
         *		- <Start_Stage1()> must be called and return true before this function can be called.
         *		- <Languages.Manager.Start_Stage1()> must be called and return true before this function can be called.
         */
        public bool Start_Stage2(Errors.ErrorList errorList)
        {
            // First we collect all the ignored keywords.

            StringSet ignoredKeywords = new StringSet(Config.KeySettingsForKeywords);

            // Remember that mergedTextConfig only has comment types, not keywords, ignored keywords, or tags.  So we have to
            // go back to the system and project configs.
            MergeIgnoredKeywordsInto(ref ignoredKeywords, systemTextConfig);
            MergeIgnoredKeywordsInto(ref ignoredKeywords, projectTextConfig);


            // Now add all keywords that aren't ignored, validating the language names along the way.

            if (!MergeKeywordsInto_Stage2(ref config, systemTextConfig, ignoredKeywords, errorList) ||
                !MergeKeywordsInto_Stage2(ref config, projectTextConfig, ignoredKeywords, errorList))
            {
                return(false);
            }


            // Now we have our final configuration and everything is okay.  Save the text files again to reformat them.

            TouchUp_Stage2(ref systemTextConfig, config);
            TouchUp_Stage2(ref projectTextConfig, config);

            Path systemTextConfigPath  = EngineInstance.Config.SystemConfigFolder + "/Comments.txt";
            Path projectTextConfigPath = EngineInstance.Config.ProjectConfigFolder + "/Comments.txt";

            ConfigFiles.TextFileParser textConfigFileParser = new ConfigFiles.TextFileParser();

            // If the project Comments.txt didn't exist, saving the blank structure that was created will create a default one.
            if (!textConfigFileParser.Save(projectTextConfigPath, PropertySource.ProjectCommentsFile, projectTextConfig, errorList))
            {
                return(false);
            }
            ;

            // We don't care if we're not able to resave the system Comments.txt since it may be in a protected location.
            textConfigFileParser.Save(systemTextConfigPath, PropertySource.SystemCommentsFile, systemTextConfig, errorList: null);


            // Save Comments.nd as well.

            Path lastRunConfigPath = EngineInstance.Config.WorkingDataFolder + "/Comments.nd";

            ConfigFiles.BinaryFileParser binaryConfigFileParser = new ConfigFiles.BinaryFileParser();

            binaryConfigFileParser.Save(lastRunConfigPath, config);


            // Look up our Group comment type ID since it's used often and we want to cache it.

            groupCommentTypeID = IDFromKeyword("group", 0);


            // Compare the config against the previous one and reparse everything if there are changes.  Changes that would invalidate
            // IDs have already been handled.

            if (config != lastRunConfig)
            {
                EngineInstance.AddStartupIssues(StartupIssues.NeedToReparseAllFiles);
            }


            // We're done with these variables which were only needed between start stages 1 and 2.

            systemTextConfig  = null;
            projectTextConfig = null;
            mergedTextConfig  = null;
            lastRunConfig     = null;

            started = true;
            return(true);
        }
Ejemplo n.º 15
0
        /* Function: Start
         *
         * Validates the configuration and starts the module if successful.  This can only be called once.  If it fails, scrap the
         * entire <Engine.Instance> and start again.
         *
         * Dependencies:
         *
         *		- <Config.Manager> and <Languages.Manager> must be started before using the rest of the class.  Also
         *		  anything that could possibly set <Config.Manager.RebuildEverything>.
         */
        public bool Start(Errors.ErrorList errors)
        {
            int           startingErrorCount = errors.Count;
            StartupIssues newStartupIssues   = StartupIssues.None;


            // Validate FileSources

            if (fileSources.Count == 0)
            {
                errors.Add(
                    Locale.Get("NaturalDocs.Engine", "Error.NoFileSourcesDefined")
                    );
            }
            else
            {
                foreach (FileSource fileSource in fileSources)
                {
                    fileSource.Validate(errors);
                }
            }


            // Make sure no source folders are completely ignored because of filters

            foreach (var fileSource in fileSources)
            {
                if (fileSource is FileSources.SourceFolder)
                {
                    FileSources.SourceFolder folderFileSource = (FileSources.SourceFolder)fileSource;

                    if (SourceFolderIsIgnored(folderFileSource.Path))
                    {
                        errors.Add(
                            Locale.Get("NaturalDocs.Engine", "Error.SourceFolderIsIgnored(sourceFolder)", folderFileSource.Path)
                            );
                    }
                }
            }


            // Load Files.nd

            if (EngineInstance.HasIssues(StartupIssues.NeedToStartFresh))
            {
                newStartupIssues |= StartupIssues.FileIDsInvalidated |
                                    StartupIssues.NeedToReparseAllFiles;
            }
            else
            {
                Files_nd filesParser = new Files_nd();

                if (!filesParser.Load(EngineInstance.Config.WorkingDataFolder + "/Files.nd", out files))
                {
                    newStartupIssues |= StartupIssues.FileIDsInvalidated |
                                        StartupIssues.NeedToReparseAllFiles;
                }
                else                 // Files.nd loaded successfully
                {
                    if (EngineInstance.HasIssues(StartupIssues.NeedToReparseAllFiles))
                    {
                        unprocessedChanges.AddChangedFiles(files.usedIDs);
                    }
                }
            }


            if (newStartupIssues != StartupIssues.None)
            {
                EngineInstance.AddStartupIssues(newStartupIssues);
            }

            bool success = (errors.Count == startingErrorCount);

            started = success;
            return(success);
        }
Ejemplo n.º 16
0
        // Group: Initialization Functions
        // __________________________________________________________________________


        /* Function: Start_Stage1
         *
         * Loads and combines the two versions of <Comments.txt>, returning whether it was successful.  If there were any
         * errors they will be added to errorList.
         *
         * Only the settings which don't depend on <Languages.txt> will be loaded.  Call <Start_Stage2()> after
         * <Languages.Manager.Start_Stage1()> has been called to complete the process.
         *
         * Dependencies:
         *
         *		- <Config.Manager> must be started before this class can start.
         */
        public bool Start_Stage1(Errors.ErrorList errorList)
        {
            StartupIssues newStartupIssues = StartupIssues.None;
            bool          success          = true;


            //
            // Comments.txt
            //

            Path systemTextConfigPath  = EngineInstance.Config.SystemConfigFolder + "/Comments.txt";
            Path projectTextConfigPath = EngineInstance.Config.ProjectConfigFolder + "/Comments.txt";
            Path oldTopicsFilePath     = EngineInstance.Config.ProjectConfigFolder + "/Topics.txt";

            ConfigFiles.TextFileParser textConfigFileParser = new ConfigFiles.TextFileParser();

            // Load the system Comments.txt.
            if (!textConfigFileParser.Load(systemTextConfigPath, PropertySource.SystemCommentsFile, errorList, out systemTextConfig))
            {
                success = false;
            }

            // Load the project Comments.txt.  We want to do this even if the system Comments.txt failed so we get the error messages
            // from both.
            if (System.IO.File.Exists(projectTextConfigPath))
            {
                if (!textConfigFileParser.Load(projectTextConfigPath, PropertySource.ProjectCommentsFile, errorList, out projectTextConfig))
                {
                    success = false;
                }
            }
            // If the project Comments.txt doesn't exist, try loading Topics.txt, which is what the file was called prior to 2.0.
            else if (System.IO.File.Exists(oldTopicsFilePath))
            {
                if (!textConfigFileParser.Load(oldTopicsFilePath, PropertySource.ProjectCommentsFile, errorList, out projectTextConfig))
                {
                    success = false;
                }
            }
            // If neither file exists just create a blank config.  The project Comments.txt not existing is not an error.
            else
            {
                projectTextConfig = new ConfigFiles.TextFile();
            }

            if (!success)
            {
                return(false);
            }

            if (!ValidateCommentTypes(systemTextConfig, errorList))
            {
                success = false;
            }
            if (!ValidateCommentTypes(projectTextConfig, errorList))
            {
                success = false;
            }

            if (!success)
            {
                return(false);
            }

            // Merge them into one combined config.  Note that this doesn't do keywords, ignored keywords, or tags.  Only the comment
            // types and their non-keyword properties will exist in the merged config.
            if (!MergeCommentTypes(systemTextConfig, projectTextConfig, out mergedTextConfig, errorList))
            {
                return(false);
            }
            if (!ValidateCommentTypes(mergedTextConfig, errorList))
            {
                return(false);
            }


            //
            // Comments.nd
            //

            Path lastRunConfigPath = EngineInstance.Config.WorkingDataFolder + "/Comments.nd";

            ConfigFiles.BinaryFileParser binaryConfigFileParser = new ConfigFiles.BinaryFileParser();

            // If we need to start fresh anyway we can skip loading the file and create a blank config
            if (EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                         StartupIssues.CommentIDsInvalidated |
                                         StartupIssues.CodeIDsInvalidated) ||

                // though if that wasn't the case but we failed at loading the file, the result is the same
                !binaryConfigFileParser.Load(lastRunConfigPath, out lastRunConfig))
            {
                lastRunConfig = new Config();

                newStartupIssues |= StartupIssues.NeedToReparseAllFiles |
                                    StartupIssues.CommentIDsInvalidated;
            }


            //
            // Create the final config
            //

            config = new Config();


            // We go through the comment types present in the merged text config files and convert them into the final config.  We
            // need the contents of Comments.nd so we can keep the comment type IDs consistent from one run to the next if possible.

            IDObjects.NumberSet usedCommentTypeIDs        = new IDObjects.NumberSet();
            IDObjects.NumberSet lastRunUsedCommentTypeIDs = lastRunConfig.UsedCommentTypeIDs();

            if (mergedTextConfig.HasCommentTypes)
            {
                foreach (var textCommentType in mergedTextConfig.CommentTypes)
                {
                    var finalCommentType = FinalizeCommentType(textCommentType);

                    // We still need to set the ID.  See if a comment type of the same name existed in the previous run.
                    var lastRunCommentType = lastRunConfig.CommentTypeFromName(textCommentType.Name);

                    // If there wasn't one we can assign a new ID, but pick one that isn't used in this run or the last run so there's no
                    // conflicts.
                    if (lastRunCommentType == null)
                    {
                        int id = lastRunUsedCommentTypeIDs.LowestAvailable;

                        if (usedCommentTypeIDs.Contains(id))
                        {
                            id = Math.Max(usedCommentTypeIDs.Highest + 1, lastRunUsedCommentTypeIDs.Highest + 1);
                        }

                        finalCommentType.ID = id;
                        config.AddCommentType(finalCommentType);

                        usedCommentTypeIDs.Add(finalCommentType.ID);
                        lastRunUsedCommentTypeIDs.Add(finalCommentType.ID);
                    }
                    // If the type did exist but we haven't used its ID yet, we can keep it.
                    else if (!usedCommentTypeIDs.Contains(lastRunCommentType.ID))
                    {
                        finalCommentType.ID = lastRunCommentType.ID;
                        config.AddCommentType(finalCommentType);

                        usedCommentTypeIDs.Add(finalCommentType.ID);
                    }
                    // However, if the type did exist and we assigned that ID already, then we have a conflict and have to tell the engine
                    // that the IDs from the last run were invalidated.  We can just assign anything unused at this point since it no longer
                    // matters.
                    else
                    {
                        finalCommentType.ID = usedCommentTypeIDs.LowestAvailable;
                        config.AddCommentType(finalCommentType);

                        usedCommentTypeIDs.Add(finalCommentType.ID);
                        newStartupIssues |= StartupIssues.CommentIDsInvalidated;
                    }

                    // Now that we have a final ID, set the simple identifier for any type that still needs it.  Some types may have it null if
                    // it wasn't manually defined and the name didn't contain A-Z characters.

                    if (finalCommentType.SimpleIdentifier == null)
                    {
                        finalCommentType.SimpleIdentifier = "CommentTypeID" + finalCommentType.ID;
                    }
                }
            }


            // Now do the same thing for tags.

            List <string> mergedTagList  = null;
            int           mergedTagCount = (systemTextConfig.HasTags ? systemTextConfig.Tags.Count : 0) +
                                           (projectTextConfig.HasTags ? projectTextConfig.Tags.Count : 0);

            if (mergedTagCount > 0)
            {
                mergedTagList = new List <string>(mergedTagCount);

                if (systemTextConfig.HasTags)
                {
                    mergedTagList.AddRange(systemTextConfig.Tags);
                }
                if (projectTextConfig.HasTags)
                {
                    mergedTagList.AddRange(projectTextConfig.Tags);
                }
            }

            IDObjects.NumberSet usedTagIDs        = new IDObjects.NumberSet();
            IDObjects.NumberSet lastRunUsedTagIDs = lastRunConfig.UsedTagIDs();

            if (mergedTagList != null)
            {
                foreach (var tagString in mergedTagList)
                {
                    // Just skip it if it already exists
                    if (config.TagFromName(tagString) != null)
                    {
                        continue;
                    }

                    var tag = new Tag(tagString);

                    // We still need to set the ID.  See if a tag of the same name existed in the previous run.
                    var lastRunTag = lastRunConfig.TagFromName(tagString);

                    // If there wasn't one we can assign a new ID, but pick one that isn't used in this run or the last run so there's no
                    // conflicts.
                    if (lastRunTag == null)
                    {
                        int id = lastRunUsedTagIDs.LowestAvailable;

                        if (usedTagIDs.Contains(id))
                        {
                            id = Math.Max(usedTagIDs.Highest + 1, lastRunUsedTagIDs.Highest + 1);
                        }

                        tag.ID = id;
                        config.AddTag(tag);

                        usedTagIDs.Add(tag.ID);
                        lastRunUsedTagIDs.Add(tag.ID);
                    }
                    // If the tag did exist but we haven't used its ID yet, we can keep it.
                    else if (!usedTagIDs.Contains(lastRunTag.ID))
                    {
                        tag.ID = lastRunTag.ID;
                        config.AddTag(tag);

                        usedTagIDs.Add(tag.ID);
                    }
                    // However, if the tag did exist and we assigned that ID already, then we have a conflict and have to tell the engine
                    // that the IDs from the last run were invalidated.  We can just assign anything unused at this point since it no longer
                    // matters.
                    else
                    {
                        tag.ID = usedTagIDs.LowestAvailable;
                        config.AddTag(tag);

                        usedTagIDs.Add(tag.ID);
                        newStartupIssues |= StartupIssues.CommentIDsInvalidated;
                    }
                }
            }


            // That's it for stage one.  Everything else is in stage 2.

            if (newStartupIssues != StartupIssues.None)
            {
                EngineInstance.AddStartupIssues(newStartupIssues);
            }

            return(true);
        }
Ejemplo n.º 17
0
        public IRuntimeContextInstance LoadScript(string path)
        {
            var compiler = EngineInstance.GetCompilerService();

            return(EngineInstance.AttachedScriptsFactory.LoadFromPath(compiler, path));
        }
Ejemplo n.º 18
0
        /* Function: Start
         *
         * Loads and combines the two versions of <Comments.txt>, returning whether it was successful.  If there were any errors
         * they will be added to errorList.
         *
         * Dependencies:
         *
         *		- <Config.Manager> must be started before this class can start.
         */
        public bool Start(Errors.ErrorList errorList)
        {
            StartupIssues newStartupIssues = StartupIssues.None;

            List <ConfigFileCommentType> systemCommentTypeList;
            List <ConfigFileCommentType> projectCommentTypeList;
            List <string> ignoredSystemKeywords;
            List <string> ignoredProjectKeywords;
            List <string> systemTags;
            List <string> projectTags;

            List <CommentType> binaryCommentTypes;
            List <Tag>         binaryTags;
            List <KeyValuePair <string, int> > binarySingularKeywords;
            List <KeyValuePair <string, int> > binaryPluralKeywords;
            List <string> binaryIgnoredKeywords;

            // The return value, which is whether we were able to successfully load and parse the system Comments.txt, and if it exists,
            // the project Comments.txt.  The project Comments.txt not existing is not a failure.
            bool success = true;

            Comments_nd commentsNDParser = new Comments_nd();


            // We need the ID numbers to stay consistent between runs, so we need to create all the comment types and tags from the
            // binary file first.  We'll worry about comparing their attributes and seeing if any were added or deleted later.

            if (EngineInstance.HasIssues(StartupIssues.NeedToStartFresh |
                                         StartupIssues.CommentIDsInvalidated))
            {
                binaryCommentTypes     = new List <CommentType>();
                binaryTags             = new List <Tag>();
                binarySingularKeywords = new List <KeyValuePair <string, int> >();
                binaryPluralKeywords   = new List <KeyValuePair <string, int> >();
                binaryIgnoredKeywords  = new List <string>();

                newStartupIssues |= StartupIssues.NeedToReparseAllFiles |
                                    StartupIssues.CommentIDsInvalidated;
            }

            else if (commentsNDParser.Load(EngineInstance.Config.WorkingDataFolder + "/Comments.nd", out binaryCommentTypes, out binaryTags,
                                           out binarySingularKeywords, out binaryPluralKeywords, out binaryIgnoredKeywords) == false)
            {
                // Even though it failed, LoadBinaryFile will still create valid empty objects for the variables.

                newStartupIssues |= StartupIssues.NeedToReparseAllFiles |
                                    StartupIssues.CommentIDsInvalidated;
            }

            else             // Load binary file succeeded
            {
                // We use a try block so if anything screwy happens, like two things having the same ID number and thus causing
                // an exception when added, we can continue as if the binary file didn't parse at all.
                try
                {
                    foreach (CommentType binaryCommentType in binaryCommentTypes)
                    {
                        // We don't add the binary comment type itself because we only want those for comparison purposes.  We want
                        // the types in commentTypes to be at their default values because the Comments.txt versions will only set some attributes,
                        // not all, and we don't want the unset attributes influenced by the binary versions.
                        CommentType newCommentType = new CommentType(binaryCommentType.Name);
                        newCommentType.ID = binaryCommentType.ID;
                        newCommentType.Flags.InBinaryFile = true;

                        commentTypes.Add(newCommentType);
                    }

                    foreach (Tag binaryTag in binaryTags)
                    {
                        Tag newTag = new Tag(binaryTag.Name);
                        newTag.ID           = binaryTag.ID;
                        newTag.InBinaryFile = true;

                        tags.Add(newTag);
                    }
                }
                catch
                {
                    newStartupIssues |= StartupIssues.NeedToReparseAllFiles |
                                        StartupIssues.CommentIDsInvalidated;

                    commentTypes.Clear();
                    tags.Clear();

                    // Clear them since they may be used later in this function.
                    binaryCommentTypes.Clear();
                    binarySingularKeywords.Clear();
                    binaryPluralKeywords.Clear();
                    binaryIgnoredKeywords.Clear();

                    // Otherwise ignore the exception and continue.
                }
            }


            Path systemFile     = EngineInstance.Config.SystemConfigFolder + "/Comments.txt";
            Path projectFile    = EngineInstance.Config.ProjectConfigFolder + "/Comments.txt";
            Path oldProjectFile = EngineInstance.Config.ProjectConfigFolder + "/Topics.txt";

            Comments_txt commentsTxtParser = new Comments_txt();


            // Load the files.

            if (!commentsTxtParser.Load(systemFile, Config.PropertySource.SystemCommentsFile,
                                        out systemCommentTypeList, out ignoredSystemKeywords, out systemTags, errorList))
            {
                success = false;
                // Continue anyway because we want to show errors from both files.
            }

            if (System.IO.File.Exists(projectFile))
            {
                if (!commentsTxtParser.Load(projectFile, Config.PropertySource.ProjectCommentsFile,
                                            out projectCommentTypeList, out ignoredProjectKeywords, out projectTags, errorList))
                {
                    success = false;
                }
            }
            else if (System.IO.File.Exists(oldProjectFile))
            {
                if (!commentsTxtParser.Load(oldProjectFile, Config.PropertySource.ProjectCommentsFile,
                                            out projectCommentTypeList, out ignoredProjectKeywords, out projectTags, errorList))
                {
                    success = false;
                }
            }
            else
            {
                // The project file not existing is not an error condition.  Fill in the variables with empty structures.
                projectCommentTypeList = new List <ConfigFileCommentType>();
                ignoredProjectKeywords = new List <string>();
                projectTags            = new List <string>();
            }

            if (success == false)
            {
                return(false);
            }


            // Combine the ignored keywords.

            StringSet ignoredKeywords = new StringSet(KeySettingsForKeywords);

            foreach (string keyword in ignoredSystemKeywords)
            {
                if (keyword != null)
                {
                    ignoredKeywords.Add(keyword);
                }
            }

            foreach (string keyword in ignoredProjectKeywords)
            {
                if (keyword != null)
                {
                    ignoredKeywords.Add(keyword);
                }
            }


            // Combine the tags

            foreach (string tagName in systemTags)
            {
                Tag tag = tags[tagName];

                if (tag == null)
                {
                    tag = new Tag(tagName);
                    tag.InSystemFile = true;
                    tags.Add(tag);
                }
                else
                {
                    tag.InSystemFile = true;

                    // In case it changed since the binary version.
                    tag.FixNameCapitalization(tagName);
                }
            }

            foreach (string tagName in projectTags)
            {
                Tag tag = tags[tagName];

                if (tag == null)
                {
                    tag = new Tag(tagName);
                    tag.InProjectFile = true;
                    tags.Add(tag);
                }
                else
                {
                    tag.InProjectFile = true;
                    tag.FixNameCapitalization(tagName);
                }
            }


            // All the comment types have to exist in IDObjects.Manager before the properties are set because Index With will need their
            // IDs.  This pass only creates the types that were not already created by the binary file.

            // We don't need to do separate passes for standard entries and alter entries because alter entries should only appear
            // in the project file and only apply to types in the system file.  Anything else is either an error (system file can't alter a
            // project entry) or would have been simplified out by LoadFile (a file with an alter entry applying to a type in the same
            // file.)

            foreach (ConfigFileCommentType commentType in systemCommentTypeList)
            {
                if (!Start_CreateType(commentType, systemFile, true, errorList))
                {
                    success = false;
                }
            }

            foreach (ConfigFileCommentType commentType in projectCommentTypeList)
            {
                if (!Start_CreateType(commentType, projectFile, false, errorList))
                {
                    success = false;
                }
            }

            // Need to exit early because Start_ApplyProperties assumes all the types were created correctly.
            if (success == false)
            {
                return(false);
            }


            // Now that everything's in commentTypes we can delete the ones that aren't in the text files, meaning they were in
            // the binary file from the last run but were deleted since then.  We have to put them on a list and delete them in a
            // second pass because deleting them while iterating through would screw up the iterator.

            List <int> deletedIDs = new List <int>();

            foreach (CommentType commentType in commentTypes)
            {
                if (commentType.Flags.InConfigFiles == false)
                {
                    deletedIDs.Add(commentType.ID);
                    newStartupIssues |= StartupIssues.NeedToReparseAllFiles;
                }
            }

            foreach (int deletedID in deletedIDs)
            {
                commentTypes.Remove(deletedID);
            }


            // Delete the tags that weren't in the text files as well.

            deletedIDs.Clear();

            foreach (Tag tag in tags)
            {
                if (tag.InConfigFiles == false)
                {
                    deletedIDs.Add(tag.ID);
                    newStartupIssues |= StartupIssues.NeedToReparseAllFiles;
                }
            }

            foreach (int deletedID in deletedIDs)
            {
                tags.Remove(deletedID);
            }


            // Fill in the properties

            foreach (ConfigFileCommentType commentType in systemCommentTypeList)
            {
                if (!Start_ApplyProperties(commentType, systemFile, ignoredKeywords, errorList))
                {
                    success = false;
                }
            }

            foreach (ConfigFileCommentType commentType in projectCommentTypeList)
            {
                if (!Start_ApplyProperties(commentType, projectFile, ignoredKeywords, errorList))
                {
                    success = false;
                }
            }

            if (success == false)
            {
                return(false);
            }


            // Make sure there are no circular dependencies in Index With.

            foreach (CommentType commentType in commentTypes)
            {
                if (commentType.Index == CommentType.IndexValue.IndexWith)
                {
                    IDObjects.NumberSet ids         = new IDObjects.NumberSet();
                    CommentType         currentType = commentType;

                    do
                    {
                        ids.Add(currentType.ID);

                        if (ids.Contains(currentType.IndexWith))
                        {
                            // Start the dependency message on the repeated comment type, not on the one the loop started with because
                            // it could go A > B > C > B, in which case reporting A is irrelevant.

                            int         repeatedID    = currentType.IndexWith;
                            CommentType iterator      = commentTypes[repeatedID];
                            string      repeatMessage = iterator.Name;

                            // We want the error message to be on the repeated type only if that's the only one: A > A.  Otherwise we
                            // want it to be the second to last one: C in A > B > C > B.
                            CommentType errorMessageTarget = currentType;

                            for (;;)
                            {
                                iterator       = commentTypes[iterator.IndexWith];
                                repeatMessage += " > " + iterator.Name;

                                if (iterator.ID == repeatedID)
                                {
                                    break;
                                }

                                errorMessageTarget = iterator;
                            }

                            Path errorMessageFile;
                            List <ConfigFileCommentType> searchList;

                            if (errorMessageTarget.Flags.InProjectFile)
                            {
                                errorMessageFile = projectFile;
                                searchList       = projectCommentTypeList;
                            }
                            else
                            {
                                errorMessageFile = systemFile;
                                searchList       = systemCommentTypeList;
                            }

                            int    errorMessageLineNumber   = 0;
                            string lcErrorMessageTargetName = errorMessageTarget.Name.ToLower();

                            foreach (ConfigFileCommentType searchListType in searchList)
                            {
                                if (searchListType.Name.ToLower() == lcErrorMessageTargetName)
                                {
                                    errorMessageLineNumber = searchListType.LineNumber;
                                    break;
                                }
                            }

                            errorList.Add(
                                Locale.Get("NaturalDocs.Engine", "Comments.txt.CircularDependencyInIndexWith(list)", repeatMessage),
                                errorMessageFile, errorMessageLineNumber
                                );

                            return(false);
                        }

                        currentType = commentTypes[currentType.IndexWith];
                    }while (currentType.Index == CommentType.IndexValue.IndexWith);
                }
            }


            // Simplify Index With.  So A > B > C becomes A > C.  Also A > B = no indexing becomes A = no indexing.

            foreach (CommentType commentType in commentTypes)
            {
                if (commentType.Index == CommentType.IndexValue.IndexWith)
                {
                    CommentType targetCommentType = commentTypes[commentType.IndexWith];

                    while (targetCommentType.Index == CommentType.IndexValue.IndexWith)
                    {
                        targetCommentType = commentTypes[targetCommentType.IndexWith];
                    }

                    if (targetCommentType.Index == CommentType.IndexValue.No)
                    {
                        commentType.Index = CommentType.IndexValue.No;
                    }
                    else
                    {
                        commentType.IndexWith = targetCommentType.ID;
                    }
                }
            }


            // Everything is okay at this point.  Save the files again to reformat them.  If the project file didn't exist, saving it
            // with the empty structures we created will create it.

            Start_FixCapitalization(systemCommentTypeList);
            Start_FixCapitalization(projectCommentTypeList);

            if (!commentsTxtParser.Save(projectFile, projectCommentTypeList, ignoredProjectKeywords, projectTags, errorList, true, false))
            {
                success = false;
            }
            ;

            if (!commentsTxtParser.Save(systemFile, systemCommentTypeList, ignoredSystemKeywords, systemTags, errorList, false, true))
            {
                success = false;
            }
            ;


            // Compare the structures with the binary ones to see if anything changed.  We only need to bother if we weren't already going to reparse
            // all the files.

            bool changed = EngineInstance.HasIssues(StartupIssues.NeedToReparseAllFiles) ||
                           (newStartupIssues & StartupIssues.NeedToReparseAllFiles) != 0;

            if (changed == false)
            {
                // First an easy comparison.

                if (binaryCommentTypes.Count != commentTypes.Count ||
                    binaryTags.Count != tags.Count ||
                    binaryIgnoredKeywords.Count != ignoredKeywords.Count ||
                    singularKeywords.Count != binarySingularKeywords.Count ||
                    pluralKeywords.Count != binaryPluralKeywords.Count)
                {
                    changed = true;
                }
            }

            if (changed == false)
            {
                // Next a detailed comparison if necessary.

                foreach (CommentType binaryCommentType in binaryCommentTypes)
                {
                    CommentType commentType = commentTypes[binaryCommentType.ID];

                    if (commentType == null || binaryCommentType != commentType)
                    {
                        changed = true;
                        break;
                    }
                }

                if (changed == false)
                {
                    foreach (Tag binaryTag in binaryTags)
                    {
                        Tag tag = tags[binaryTag.ID];

                        if (tag == null || binaryTag != tag)
                        {
                            changed = true;
                            break;
                        }
                    }
                }

                if (changed == false)
                {
                    foreach (string binaryIgnoredKeyword in binaryIgnoredKeywords)
                    {
                        if (!ignoredKeywords.Contains(binaryIgnoredKeyword))
                        {
                            changed = true;
                            break;
                        }
                    }
                }

                if (changed == false)
                {
                    foreach (KeyValuePair <string, int> binarySingularKeywordPair in binarySingularKeywords)
                    {
                        // We can use ID instead of Name because we know they match now.
                        if (singularKeywords.ContainsKey(binarySingularKeywordPair.Key) == false ||
                            singularKeywords[binarySingularKeywordPair.Key].ID != binarySingularKeywordPair.Value)
                        {
                            changed = true;
                            break;
                        }
                    }
                }

                if (changed == false)
                {
                    foreach (KeyValuePair <string, int> binaryPluralKeywordPair in binaryPluralKeywords)
                    {
                        // We can use ID instead of Name because we know they match now.
                        if (pluralKeywords.ContainsKey(binaryPluralKeywordPair.Key) == false ||
                            pluralKeywords[binaryPluralKeywordPair.Key].ID != binaryPluralKeywordPair.Value)
                        {
                            changed = true;
                            break;
                        }
                    }
                }
            }

            if (changed)
            {
                newStartupIssues |= StartupIssues.NeedToReparseAllFiles;
            }


            commentsNDParser.Save(EngineInstance.Config.WorkingDataFolder + "/Comments.nd",
                                  commentTypes, tags, singularKeywords, pluralKeywords, ignoredKeywords);

            groupCommentTypeID = IDFromKeyword("group");

            if (newStartupIssues != StartupIssues.None)
            {
                EngineInstance.AddStartupIssues(newStartupIssues);
            }

            return(success);
        }
Ejemplo n.º 19
0
        /// <summary>
        /// Creates an engine instance.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public IEngineInstance CreateInstance(string name)
        {
            EngineInstance engineInstance = new EngineInstance{ Name = name };
            lock (_engineInstanceTable)
            {
                _engineInstanceTable[engineInstance.Id] = engineInstance;
            }

            if (InstanceCreated != null)
            {
                InstanceCreated(this, new InstanceEventArgs(engineInstance));
            }

            return engineInstance;
        }
Ejemplo n.º 20
0
 public void CanLoad()
 {
     EngineInstance.EnsureLoaded();
 }
Ejemplo n.º 21
0
		private static bool BuildDocumentation (ErrorList errorList)
			{
			ShowConsoleHeader();

			bool rebuildAllOutputFromCommandLine = EngineInstance.Config.RebuildAllOutput;
			bool reparseEverythingFromCommandLine = EngineInstance.Config.ReparseEverything;

			EngineInstance.AddStartupWatcher(new EngineStartupWatcher());


			executionTimer.Start("Engine Startup");
			
			if (EngineInstance.Start(errorList, commandLineConfig) == true)
				{
				executionTimer.End("Engine Startup");


				// File Search
						
				executionTimer.Start("Finding Source Files");

				using ( StatusManagers.FileSearch statusManager = new StatusManagers.FileSearch() )
					{
					statusManager.Start();
							
					Multithread("File Adder", EngineInstance.Files.WorkOnAddingAllFiles);
							
					statusManager.End();
					}
							
				EngineInstance.Files.DeleteFilesNotInFileSources( Engine.Delegates.NeverCancel );
							
				executionTimer.End("Finding Source Files");

						
				// Rebuild notice

				string alternateStartMessage = null;
						
				if (reparseEverythingFromCommandLine || rebuildAllOutputFromCommandLine)
					{  alternateStartMessage = "Status.RebuildEverythingByRequest";  }
				else if (EngineInstance.Config.ReparseEverything && EngineInstance.Config.RebuildAllOutput)
					{  alternateStartMessage = "Status.RebuildEverythingAutomatically";  }
							
							
				// Parsing
						
				executionTimer.Start("Parsing Source Files");

				using ( StatusManagers.Parsing statusManager = new StatusManagers.Parsing(alternateStartMessage) )
					{
					statusManager.Start();
					totalFileChanges = statusManager.TotalFilesToProcess;

					Multithread("Parser", EngineInstance.Files.WorkOnProcessingChanges);							
							
					statusManager.End();
					}
							
				executionTimer.End("Parsing Source Files");

							
				// Resolving
						
				executionTimer.Start("Resolving Links");

				using ( StatusManagers.ResolvingLinks statusManager = new StatusManagers.ResolvingLinks() )
					{
					statusManager.Start();

					Multithread("Resolver", EngineInstance.Links.WorkOnResolvingLinks);
							
					statusManager.End();
					}
							
				executionTimer.End("Resolving Links");

							
				// Building
						
				executionTimer.Start("Building Output");

				using ( StatusManagers.Building statusManager = new StatusManagers.Building() )
					{
					statusManager.Start();

					Multithread("Builder", EngineInstance.Output.WorkOnUpdatingOutput);
					Multithread("Finalizer", EngineInstance.Output.WorkOnFinalizingOutput);							
							
					statusManager.End();
					}
							
				executionTimer.End("Building Output");

							
				// End
						
				EngineInstance.Cleanup(Delegates.NeverCancel);
						
				ShowConsoleFooter(true);
				return true;
				}

			else // engine did not start correctly
				{  
				executionTimer.End("Engine Startup");

				ShowConsoleFooter(false);
				return false;
				}
			}
Ejemplo n.º 22
0
        public void AttachAddIn(string dllPath)
        {
            var assembly = System.Reflection.Assembly.LoadFrom(dllPath);

            EngineInstance.AttachAssembly(assembly, EngineInstance.Environment);
        }
Ejemplo n.º 23
0
        /* Function: HandleException
         */
        static private void HandleException(Exception e)
        {
            var errorOutput = Console.Error;

            errorOutput.Write("\n\n------------------------------------------------------------\n");
            errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.CLI", "Crash.Exception",
                                                 "Natural Docs has closed because of the following error:"));
            errorOutput.WriteLine();
            errorOutput.WriteLine(e.Message);


            // If it's not a user friendly exception or a thread exception wrapping a user friendly exception...
            if (e.GetType() != typeof(Engine.Exceptions.UserFriendly) &&
                (e.GetType() == typeof(Engine.Exceptions.Thread) &&
                 e.InnerException.GetType() == typeof(Engine.Exceptions.UserFriendly)) == false)
            {
                Engine.Path crashFile = EngineInstance.BuildCrashReport(e);

                if (crashFile != null)
                {
                    errorOutput.WriteLine();
                    errorOutput.Write(Locale.SafeGet("NaturalDocs.CLI", "Crash.ReportAt(file).multiline",
                                                     "A crash report has been generated at {0}.\n" +
                                                     "Please include this file when asking for help at naturaldocs.org.\n", crashFile));
                }

                else
                {
                    errorOutput.WriteLine();
                    errorOutput.WriteLine(e.StackTrace);

                    // If it's a thread exception, skip the first inner one because that's the wrapped one, which we already got the
                    // message for.
                    if (e is Engine.Exceptions.Thread)
                    {
                        e = e.InnerException;
                    }

                    while (e.InnerException != null)
                    {
                        e = e.InnerException;

                        errorOutput.WriteLine();
                        errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.CLI", "Crash.NestedException",
                                                             "This error was caused by the following error:") + "\n");

                        errorOutput.WriteLine(e.Message);
                    }

                    try
                    {
                        errorOutput.WriteLine();
                        errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.CLI", "Crash.Version", "Version") +
                                              ": " + Engine.Instance.VersionString);
                        errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.CLI", "Crash.Platform", "Platform") +
                                              ": " + Environment.OSVersion.VersionString +
                                              " (" + Environment.OSVersion.Platform + ")");
                        errorOutput.WriteLine("SQLite: " + Engine.SQLite.API.LibVersion());
                        errorOutput.WriteLine();
                        errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.CLI", "Crash.CommandLine", "Command Line") + ":");
                        errorOutput.WriteLine();
                        errorOutput.WriteLine("   " + Environment.CommandLine);
                    }
                    catch
                    {
                    }

                    errorOutput.WriteLine();
                    errorOutput.WriteLine(Locale.SafeGet("NaturalDocs.CLI", "Crash.IncludeInfoAndGetHelp",
                                                         "Please include this information when asking for help at naturaldocs.org."));
                }
            }

            errorOutput.Write("\n------------------------------------------------------------\n\n");
        }