/* Function: Validate * Checks whether the file source is valid and adds an error to the list if not. The default implementation simply * returns true. */ virtual public bool Validate(Errors.ErrorList errors) { return(true); }
/* Function: LoadStyle * Converts a style path to a <HTMLStyle> object, loading its <Style.txt> if appropriate. If there are any errors found * in <Style.txt> they will be added to the list and the function will return null. */ public static HTMLStyle LoadStyle(Path stylePath, Errors.ErrorList errors) { HTMLStyle style = new HTMLStyle(stylePath); if (style.IsCSSOnly) { return(style); } using (ConfigFile file = new ConfigFile()) { if (!file.Open(stylePath, ConfigFile.FileFormatFlags.MakeIdentifiersLowercase | ConfigFile.FileFormatFlags.CondenseIdentifierWhitespace, errors)) { return(null); } int errorCount = errors.Count; string lcIdentifier, value; System.Text.RegularExpressions.Match match; Regex.Styles.HTML.Inherit inheritRegex = new Regex.Styles.HTML.Inherit(); Regex.Styles.HTML.Link linkRegex = new Regex.Styles.HTML.Link(); Regex.Styles.HTML.OnLoad onLoadRegex = new Regex.Styles.HTML.OnLoad(); Regex.Styles.HTML.LinkableFileExtensions linkableFileExtensionsRegex = new Regex.Styles.HTML.LinkableFileExtensions(); while (file.Get(out lcIdentifier, out value)) { if (inheritRegex.IsMatch(lcIdentifier)) { style.AddInheritedStyle(value); continue; } match = linkRegex.Match(lcIdentifier); if (match.Success) { PageType pageType = PageType.All; if (match.Groups[1].Success) { PageType?pageTypeTemp = PageTypeOf(match.Groups[1].ToString()); if (pageTypeTemp == null) { file.AddError(Locale.Get("NaturalDocs.Engine", "ConfigFile.NotAValidIdentifier(identifier)", lcIdentifier)); } else { pageType = pageTypeTemp.Value; } } Path linkedFile = value; if (linkableFileExtensionsRegex.IsMatch(linkedFile.Extension)) { Path fullLinkedFile = style.Folder + "/" + linkedFile; if (System.IO.File.Exists(fullLinkedFile)) { style.AddLinkedFile(linkedFile, pageType); } else { file.AddError(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.CantFindLinkedFile(name)", fullLinkedFile)); } } else { file.AddError(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.CantLinkFileWithExtension(extension)", linkedFile.Extension)); } continue; } match = onLoadRegex.Match(lcIdentifier); if (match.Success) { PageType pageType = PageType.All; if (match.Groups[1].Success) { PageType?pageTypeTemp = PageTypeOf(match.Groups[1].ToString()); if (pageTypeTemp == null) { file.AddError(Locale.Get("NaturalDocs.Engine", "ConfigFile.NotAValidIdentifier(identifier)", lcIdentifier)); } else { pageType = pageTypeTemp.Value; } } style.AddOnLoad(value, pageType); continue; } file.AddError(Locale.Get("NaturalDocs.Engine", "ConfigFile.NotAValidIdentifier(identifier)", lcIdentifier)); } if (errorCount == errors.Count) { return(style); } else { return(null); } } }
/* Function: SaveStyle * Saves the passed <HTMLStyle> as <Style.txt>, provided it's not CSS-only. */ static public bool SaveStyle(HTMLStyle style, Errors.ErrorList errorList, bool noErrorOnFail) { if (style.IsCSSOnly) { throw new InvalidOperationException(); } System.Text.StringBuilder output = new System.Text.StringBuilder(512); // Header output.AppendLine("Format: " + Engine.Instance.VersionString); output.AppendLine(); output.Append(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.Header.multiline")); output.AppendLine(); output.AppendLine(); // Inheritance output.Append(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.InheritanceHeader.multiline")); output.AppendLine(); if (style.Inherits != null) { foreach (string inheritedStyleName in style.Inherits) { output.Append("Inherit: "); output.AppendLine(inheritedStyleName); } output.AppendLine(); output.AppendLine(); } output.Append(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.InheritanceReference.multiline")); output.AppendLine(); output.AppendLine(); // Linked Files output.Append(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.LinkedFilesHeader.multiline")); output.AppendLine(); if (style.Links != null) { foreach (HTMLStyleFileLink link in style.Links) { if (link.Type != PageType.All) { output.Append(PageTypeNameOf(link.Type)); output.Append(' '); } output.Append("Link: "); output.AppendLine(link.File); } output.AppendLine(); output.AppendLine(); } output.Append(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.LinkedFilesReference.multiline")); output.AppendLine(); output.AppendLine(); // OnLoad output.Append(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.OnLoadHeader.multiline")); output.AppendLine(); if (style.OnLoad != null) { foreach (HTMLStyleOnLoadStatement onLoadStatement in style.OnLoad) { if (onLoadStatement.Type != PageType.All) { output.Append(PageTypeNameOf(onLoadStatement.Type)); output.Append(' '); } output.Append("OnLoad: "); output.AppendLine(onLoadStatement.Statement); output.AppendLine(); } output.AppendLine(); } output.Append(Locale.Get("NaturalDocs.Engine", "HTML.Style.txt.OnLoadReference.multiline")); return(ConfigFile.SaveIfDifferent(style.ConfigFile, output.ToString(), noErrorOnFail, errorList)); }
/* 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) { 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; // Whether anything has changed since the last run, as determined by Languages.nd. If Languages.nd doesn't exist // or is corrupt, we have to assume something changed. bool changed = false; // 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.Config.ReparseEverything == true) { 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>(); changed = true; } else if (languagesNDParser.Load(EngineInstance.Config.WorkingDataFolder + "/Languages.nd", out binaryLanguages, out binaryAliases, out binaryExtensions, out binaryShebangStrings, out binaryIgnoredExtensions) == false) { changed = true; // 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(); changed = true; 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, 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, 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) { changed = true; } } } 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) { changed = true; } else if (changed == false) { // Do a detailed comparison now. 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; } } } } languagesNDParser.Save(EngineInstance.Config.WorkingDataFolder + "/Languages.nd", languages, aliases, extensions, shebangStrings, ignoredExtensions); if (success == true && changed == true) { EngineInstance.Config.ReparseEverything = true; } return(success); }
/* Function: Start_CantDefinePropertyError * A helper function used only by <Start()> and its other helper functions which adds an error saying the passed * property cannot be defined for the current language type. */ private void Start_CantDefinePropertyError(ConfigFileLanguage configFileLanguage, Language.LanguageType type, Path sourceFile, string propertyName, Errors.ErrorList errorList) { string typeString; if (type == Language.LanguageType.TextFile) { typeString = "TextFiles"; } else if (type == Language.LanguageType.Container) { typeString = "Containers"; } else if (type == Language.LanguageType.FullSupport) { typeString = "FullLanguageSupport"; } else // BasicSupport { typeString = "BasicLanguageSupport"; } errorList.Add( Locale.Get("NaturalDocs.Engine", "Languages.txt.CantDefinePropertyFor" + typeString + "(property, language)", propertyName, configFileLanguage.Name), sourceFile, configFileLanguage.LineNumber ); }
/* Function: Start_AddLanguage * A helper function that is used only by <Start()> to add a <ConfigFileLanguage> into <languages>. * Returns whether it was able to do so without any errors. */ private bool Start_AddLanguage(ConfigFileLanguage configFileLanguage, Path sourceFile, bool isSystemFile, StringSet ignoredExtensions, Errors.ErrorList errorList) { bool success = true; // Validate or create the language. if (configFileLanguage.AlterLanguage == true) { // If altering a language that doesn't exist at all, at least not in the config files... if (languages.Contains(configFileLanguage.Name) == false || languages[configFileLanguage.Name].InConfigFiles == false) { errorList.Add( Locale.Get("NaturalDocs.Engine", "Languages.txt.AlteredLanguageDoesntExist(name)", configFileLanguage.Name), sourceFile, configFileLanguage.LineNumber ); success = false; } } else // define language, not alter { // Error if defining a language that already exists in the config files. Having it exist otherwise is fine. if (languages.Contains(configFileLanguage.Name)) { if (languages[configFileLanguage.Name].InConfigFiles == true) { errorList.Add( Locale.Get("NaturalDocs.Engine", "Languages.txt.LanguageAlreadyExists(name)", configFileLanguage.Name), sourceFile, configFileLanguage.LineNumber ); success = false; } } else { Language newLanguage = new Language(this, configFileLanguage.Name); languages.Add(newLanguage); } if (isSystemFile) { languages[configFileLanguage.Name].InSystemFile = true; } else { languages[configFileLanguage.Name].InProjectFile = true; } } if (success == false) { return(false); } // Apply the properties. Language language = languages[configFileLanguage.Name]; if (configFileLanguage.SimpleIdentifier != null) { language.SimpleIdentifier = configFileLanguage.SimpleIdentifier; } if (configFileLanguage.LineCommentStrings != null) { if (language.Type != Language.LanguageType.BasicSupport) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Line Comment", errorList); success = false; } else { language.LineCommentStrings = configFileLanguage.LineCommentStrings; } } if (configFileLanguage.BlockCommentStringPairs != null) { if (language.Type != Language.LanguageType.BasicSupport) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Block Comment", errorList); success = false; } else { language.BlockCommentStringPairs = configFileLanguage.BlockCommentStringPairs; } } if (configFileLanguage.MemberOperator != null) { if (language.Type != Language.LanguageType.BasicSupport && language.Type != Language.LanguageType.TextFile) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Member Operator", errorList); success = false; } else { language.MemberOperator = configFileLanguage.MemberOperator; } } if (configFileLanguage.LineExtender != null) { if (language.Type != Language.LanguageType.BasicSupport) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Line Extender", errorList); success = false; } else { language.LineExtender = configFileLanguage.LineExtender; } } if (configFileLanguage.EnumValue != null) { if (language.Type != Language.LanguageType.BasicSupport && language.Type != Language.LanguageType.TextFile) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Enum Value", errorList); success = false; } else { language.EnumValue = (Language.EnumValues)configFileLanguage.EnumValue; } } if (configFileLanguage.CaseSensitive != null) { if (language.Type != Language.LanguageType.BasicSupport && language.Type != Language.LanguageType.TextFile) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Case Sensitive", errorList); success = false; } else { language.CaseSensitive = (bool)configFileLanguage.CaseSensitive; } } string[] commentTypeNamesWithPrototypeEnders = configFileLanguage.GetCommentTypeNamesWithPrototypeEnders(); if (commentTypeNamesWithPrototypeEnders != null) { if (language.Type != Language.LanguageType.BasicSupport) { Start_CantDefinePropertyError(configFileLanguage, language.Type, sourceFile, "Prototype Enders", errorList); success = false; } else { foreach (string commentTypeName in commentTypeNamesWithPrototypeEnders) { CommentTypes.CommentType commentType = EngineInstance.CommentTypes.FromName(commentTypeName); if (commentType == null) { errorList.Add( Locale.Get("NaturalDocs.Engine", "Languages.txt.PrototypeEnderCommentTypeDoesntExist(name)", commentTypeName), sourceFile, configFileLanguage.LineNumber ); success = false; } else { string[] prototypeEnderStrings = configFileLanguage.GetPrototypeEnderStrings(commentTypeName); PrototypeEnders prototypeEnders = new PrototypeEnders(prototypeEnderStrings); language.SetPrototypeEnders(commentType.ID, prototypeEnders); } } } } // Apply the aliases, extensions, and shebang strings. if (configFileLanguage.Aliases != null) { // If using Replace Aliases, find all existing aliases pointing to this language and remove them. if (configFileLanguage.AlterLanguage == true && configFileLanguage.AddAliases == false) { List <string> removedAliases = new List <string>(); foreach (KeyValuePair <string, Language> pair in aliases) { if ((object)pair.Value == (object)language) { removedAliases.Add(pair.Key); } } foreach (string removedAlias in removedAliases) { aliases.Remove(removedAlias); } } // Add new aliases. foreach (string alias in configFileLanguage.Aliases) { aliases[alias] = language; } } if (configFileLanguage.Extensions != null) { // If using Replace Extensions, find all existing extensions pointing to this language and remove them. if (configFileLanguage.AlterLanguage == true && configFileLanguage.AddExtensions == false) { List <string> removedExtensions = new List <string>(); foreach (KeyValuePair <string, Language> pair in extensions) { if ((object)pair.Value == (object)language) { removedExtensions.Add(pair.Key); } } foreach (string removedExtension in removedExtensions) { extensions.Remove(removedExtension); } } // Add new extensions. foreach (string extension in configFileLanguage.Extensions) { if (ignoredExtensions.Contains(extension) == false) { extensions[extension] = language; } } } if (configFileLanguage.ShebangStrings != null) { // If using Replace Shebang Strings, find all existing shebang strings pointing to this language and remove them. if (configFileLanguage.AlterLanguage == true && configFileLanguage.AddShebangStrings == false) { List <string> removedShebangStrings = new List <string>(); foreach (KeyValuePair <string, Language> pair in shebangStrings) { if ((object)pair.Value == (object)language) { removedShebangStrings.Add(pair.Key); } } foreach (string removedShebangString in removedShebangStrings) { shebangStrings.Remove(removedShebangString); } } // Add new shebang strings. foreach (string shebangString in configFileLanguage.ShebangStrings) { shebangStrings[shebangString] = language; } } return(success); }