Exemple #1
0
        /* Function: MergeLanguages
         *
         * Merges two <ConfigFiles.TextFiles> into a new one, putting all the languages into one list and applying any alter entries.
         * This does NOT cover file extensions, aliases, or shebang strings; those will be blank in the result.  Returns the new
         * list and whether it was successful.
         *
         * Any errors will be added to errorList, such as defining a duplicate entry that doesn't use alter, or an alter entry for a
         * non-existent language.  All alter entries will be applied, including any appearing in the base config, so there will only be
         * non-alter entries in the returned list.
         */
        protected bool MergeLanguages(ConfigFiles.TextFile baseConfig, ConfigFiles.TextFile overridingConfig,
                                      out ConfigFiles.TextFile combinedConfig, Errors.ErrorList errorList)
        {
            combinedConfig = new ConfigFiles.TextFile();

            // We merge the base config into the empty config instead of just copying it so any alter entries it has are applied
            if (!MergeLanguagesInto(ref combinedConfig, baseConfig, errorList) ||
                !MergeLanguagesInto(ref combinedConfig, overridingConfig, errorList))
            {
                combinedConfig = null;
                return(false);
            }

            return(true);
        }
Exemple #2
0
        // Group: Initialization Support Functions
        // __________________________________________________________________________
        //
        // These functions are only used by <Start_Stage1()> and <Start_Stage2()>.
        //


        /* Function: ValidateLanguages
         * Validates all the language settings in a <ConfigFiles.TextFile>.  Returns whether it is valid, and adds any errors it
         * finds to errorList.
         */
        protected bool ValidateLanguages(ConfigFiles.TextFile configFile, Errors.ErrorList errorList)
        {
            bool success = true;

            if (configFile.HasLanguages)
            {
                foreach (var language in configFile.Languages)
                {
                    if (!ValidateLanguage(language, errorList))
                    {
                        success = false;
                        // Continue anyway so we can report the errors in all of them.
                    }
                }
            }

            return(success);
        }
Exemple #3
0
        /* Function: MergePrototypeEndersInto_Stage2
         * Merges the prototype enders found in the <ConfigFiles.TextFile> into the <Languages> of the <Config>, returning whether
         * it was successful.  If not it will add any errors into the error list.  This must be done in <Start_Stage2()> because we need
         * <CommentTypes.Manager> to have loaded all the comment type names.
         */
        protected bool MergePrototypeEndersInto_Stage2(ref Config config, ConfigFiles.TextFile textFileConfig,
                                                       Errors.ErrorList errorList)
        {
            bool success = true;

            if (textFileConfig.HasLanguages)
            {
                foreach (var textFileLanguage in textFileConfig.Languages)
                {
                    if (textFileLanguage.HasPrototypeEnders)
                    {
                        var matchingLanguage = config.LanguageFromName(textFileLanguage.Name);

                                                #if DEBUG
                        if (matchingLanguage == null)
                        {
                            throw new InvalidOperationException();
                        }
                                                #endif

                        foreach (var textFilePrototypeEnders in textFileLanguage.PrototypeEnders)
                        {
                            var matchingCommentType =
                                EngineInstance.CommentTypes.FromName(textFilePrototypeEnders.CommentType);

                            if (matchingCommentType == null)
                            {
                                errorList.Add(Locale.Get("NaturalDocs.Engine", "Languages.txt.PrototypeEnderCommentTypeDoesntExist(name)", textFilePrototypeEnders.CommentType),
                                              textFilePrototypeEnders.PropertyLocation);
                                success = false;
                            }
                            else
                            {
                                matchingLanguage.AddPrototypeEnders(
                                    new PrototypeEnders(matchingCommentType.ID, textFilePrototypeEnders.EnderStrings)
                                    );
                            }
                        }
                    }
                }
            }

            return(success);
        }
Exemple #4
0
        /* Function: TouchUp_Stage2
         * Applies some minor improvements to the <ConfigFiles.TextFile>, such as making sure the capitalization of Alter Language
         * and [Comment Type] Prototype Enders match the original definition.  Assumes everything is valid, meaning all Alter
         * Language entries have corresponding entries in finalConfig and all [Comment Type] Prototype Enders entries have
         * corresponding languages in <CommentTypes.Manager>.
         */
        protected void TouchUp_Stage2(ref ConfigFiles.TextFile textConfig, Config finalConfig)
        {
            if (textConfig.HasLanguages)
            {
                foreach (var textFileLanguage in textConfig.Languages)
                {
                    // Fix "Alter Language: [name]" capitalization

                    if (textFileLanguage.AlterLanguage)
                    {
                        var originalLanguage = finalConfig.LanguageFromName(textFileLanguage.Name);
                        textFileLanguage.FixNameCapitalization(originalLanguage.Name);

                        // We don't also check to see if the language we're altering exists in the same file and merge their definitions
                        // into one.  Why?  Consider this:
                        //
                        // Language: Language A
                        //    Extensions: langA
                        //
                        // Language: Language B
                        //    Extensions: langB
                        //
                        // Alter Language: Language A
                        //    Add Extensions: langB
                        //
                        // File extensions B should be part of Language A.  However, if we merged the definitions it would appear first
                        // and be overridden by Language B.  So we just leave the two language entries for A instead.
                    }


                    if (textFileLanguage.HasPrototypeEnders)
                    {
                        foreach (var textFilePrototypeEnders in textFileLanguage.PrototypeEnders)
                        {
                            // Fix "[Comment Type] Prototype Enders" capitalization

                            var originalCommentType = EngineInstance.CommentTypes.FromName(textFilePrototypeEnders.CommentType);
                            textFilePrototypeEnders.CommentType = originalCommentType.Name;
                        }
                    }
                }
            }
        }
Exemple #5
0
        /* Function: MergeLanguagesInto
         * Merges the languages of the second <ConfigFiles.TextFile> into the first, adding new types and applying any alter entries.
         * This does NOT merge file extensions, aliases, or shebang strings.  The base config will be changed, even if there are errors.
         * Returns false if there were any errors and adds them to errorList.
         */
        protected bool MergeLanguagesInto(ref ConfigFiles.TextFile baseConfig, ConfigFiles.TextFile overridingConfig,
                                          Errors.ErrorList errorList)
        {
            bool success = true;

            if (overridingConfig.HasLanguages)
            {
                foreach (var overridingLanguage in overridingConfig.Languages)
                {
                    var matchingLanguage = baseConfig.FindLanguage(overridingLanguage.Name);

                    if (matchingLanguage != null)
                    {
                        if (overridingLanguage.AlterLanguage == false)
                        {
                            errorList.Add(Locale.Get("NaturalDocs.Engine", "Languages.txt.LanguageAlreadyExists(name)", overridingLanguage.Name),
                                          overridingLanguage.PropertyLocation);
                            success = false;
                        }
                        else
                        {
                            MergeLanguageInto(ref matchingLanguage, overridingLanguage);
                        }
                    }

                    else                     // no match
                    {
                        if (overridingLanguage.AlterLanguage == true)
                        {
                            errorList.Add(Locale.Get("NaturalDocs.Engine", "Languages.txt.AlteredLanguageDoesntExist(name)", overridingLanguage.Name),
                                          overridingLanguage.NamePropertyLocation);
                            success = false;
                        }
                        else
                        {
                            baseConfig.AddLanguage(overridingLanguage.Duplicate());
                        }
                    }
                }
            }

            return(success);
        }
Exemple #6
0
        /* Function: MergeLanguageIdentifiersInto
         * Merges the file extensions, aliases, and shebang strings from the <ConfigFiles.TextFiles> into the <Config>.  It assumes all
         * <ConfigFiles.TextFileLanguages> in textConfig have corresponding <Language> in outputConfig.
         */
        protected void MergeLanguageIdentifiersInto(ref Config outputConfig, ConfigFiles.TextFile systemTextConfig,
                                                    ConfigFiles.TextFile projectTextConfig)
        {
            // First collect our ignored extensions

            StringSet ignoredFileExtensions = new StringSet(Config.KeySettingsForFileExtensions);

            if (systemTextConfig.HasIgnoredFileExtensions)
            {
                foreach (var ignoredFileExtension in systemTextConfig.IgnoredFileExtensions)
                {
                    ignoredFileExtensions.Add(ignoredFileExtension);
                }
            }
            if (projectTextConfig.HasIgnoredFileExtensions)
            {
                foreach (var ignoredFileExtension in projectTextConfig.IgnoredFileExtensions)
                {
                    ignoredFileExtensions.Add(ignoredFileExtension);
                }
            }


            // Now turn our language lists into one big combined one, but not in a way that merges any of its entries like
            // mergedTextConfig did.  Just put them all one after the other.

            int languageEntryCount = (systemTextConfig.HasLanguages ? systemTextConfig.Languages.Count : 0) +
                                     (projectTextConfig.HasLanguages ? projectTextConfig.Languages.Count : 0);

            List <ConfigFiles.TextFileLanguage> languages = new List <ConfigFiles.TextFileLanguage>(languageEntryCount);

            if (systemTextConfig.HasLanguages)
            {
                languages.AddRange(systemTextConfig.Languages);
            }
            if (projectTextConfig.HasLanguages)
            {
                languages.AddRange(projectTextConfig.Languages);
            }


            // Now apply file extensions, aliases, and shebang strings.  We do it from this list instead of mergedTextConfig so
            // so everything happens in the proper order.  For example:
            //
            // Language: LanguageA
            //    Extensions: langA
            //
            // Language: LanguageB
            //    Extensions: langB
            //
            // Alter Language: LanguageA
            //    Replace Extensions: langB
            //
            // In this case langB should actually map to LanguageA.  Not only that, langA should not be applied at all because
            // we used Replace instead of Add.

            for (int i = 0; i < languageEntryCount; i++)
            {
                // We don't need to check whether they're defined for the first time, added, or replaced here.  In all cases we would
                // apply them unless there's a future entry that says Replace.
                bool applyFileExtensions = languages[i].HasFileExtensions;
                bool applyAliases        = languages[i].HasAliases;
                bool applyShebangStrings = languages[i].HasShebangStrings;

                // Check for future Replace entries.
                string normalizedLanguageName = languages[i].Name.NormalizeKey(Config.KeySettingsForLanguageName);

                for (int j = i + 1; j < languageEntryCount; j++)
                {
                    if (!applyFileExtensions && !applyAliases && !applyShebangStrings)
                    {
                        break;
                    }

                    if (languages[j].Name.NormalizeKey(Config.KeySettingsForLanguageName) == normalizedLanguageName)
                    {
                        if (languages[j].HasFileExtensions &&
                            languages[j].FileExtensionsPropertyChange == ConfigFiles.TextFileLanguage.PropertyChange.Replace)
                        {
                            applyFileExtensions = false;
                        }

                        if (languages[j].HasAliases &&
                            languages[j].AliasesPropertyChange == ConfigFiles.TextFileLanguage.PropertyChange.Replace)
                        {
                            applyAliases = false;
                        }

                        if (languages[j].HasShebangStrings &&
                            languages[j].ShebangStringsPropertyChange == ConfigFiles.TextFileLanguage.PropertyChange.Replace)
                        {
                            applyShebangStrings = false;
                        }
                    }
                }

                // Apply what's left.
                int languageID = outputConfig.LanguageFromName(languages[i].Name).ID;

                                #if DEBUG
                if (languageID == 0)
                {
                    throw new InvalidOperationException();
                }
                                #endif

                if (applyFileExtensions)
                {
                    foreach (var fileExtension in languages[i].FileExtensions)
                    {
                        if (!ignoredFileExtensions.Contains(fileExtension))
                        {
                            outputConfig.AddFileExtension(fileExtension, languageID);
                        }
                    }
                }

                if (applyAliases)
                {
                    foreach (var alias in languages[i].Aliases)
                    {
                        outputConfig.AddAlias(alias, languageID);
                    }
                }

                if (applyShebangStrings)
                {
                    foreach (var shebangString in languages[i].ShebangStrings)
                    {
                        outputConfig.AddShebangString(shebangString, languageID);
                    }
                }
            }
        }
Exemple #7
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);
        }