コード例 #1
0
ファイル: Manager.Start.cs プロジェクト: flibX0r/NaturalDocs
        // 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);
        }
コード例 #2
0
ファイル: Manager.Start.cs プロジェクト: flibX0r/NaturalDocs
        /* 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);
        }