/* Function: Load * Loads <Style.txt> and returns it as a <Styles.Advanced> object. If there are any errors found in <Style.txt> they will be added * to the list and the function will return null. */ public Styles.Advanced Load(Path path, Errors.ErrorList errors) { Styles.Advanced style = new Styles.Advanced(path); using (ConfigFile file = new ConfigFile()) { if (!file.Open(path, Config.PropertySource.StyleConfigurationFile, ConfigFile.FileFormatFlags.MakeIdentifiersLowercase | ConfigFile.FileFormatFlags.CondenseIdentifierWhitespace, errors)) { return(null); } int errorCount = errors.Count; string lcIdentifier, value; System.Text.RegularExpressions.Match match; while (file.Get(out lcIdentifier, out value)) { // Inherit if (inheritRegex.IsMatch(lcIdentifier)) { style.AddInheritedStyle(value, file.PropertyLocation, null); continue; } // Link match = linkRegex.Match(lcIdentifier); if (match.Success) { PageType pageType = PageType.All; if (match.Groups[1].Success) { PageType?pageTypeTemp = PageTypes.FromName(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 (!Styles.Manager.LinkableFileExtensions.Contains(linkedFile.Extension)) { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.CantLinkFileWithExtension(extension)", linkedFile.Extension)); } else if (linkedFile.Extension.ToLower() == "css" && pageType != PageType.All) { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.CantLinkCSSFileToSpecificPageTypes(pageType)", PageTypes.NameOf(pageType))); } else { if (linkedFile.IsRelative) { linkedFile = style.Folder + "/" + linkedFile; } if (!System.IO.File.Exists(linkedFile)) { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.CantFindLinkedFile(name)", linkedFile)); } else if (!style.Folder.Contains(linkedFile)) { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.LinkedFileMustBeInStyleFolder(name, folder)", linkedFile, style.Folder)); } else { style.AddLinkedFile(linkedFile, file.PropertyLocation, pageType); } } continue; } // OnLoad match = onLoadRegex.Match(lcIdentifier); if (match.Success) { PageType pageType = PageType.All; if (match.Groups[1].Success) { PageType?pageTypeTemp = PageTypes.FromName(match.Groups[1].ToString()); if (pageTypeTemp == null) { file.AddError(Locale.Get("NaturalDocs.Engine", "ConfigFile.NotAValidIdentifier(identifier)", lcIdentifier)); } else { pageType = pageTypeTemp.Value; } } style.AddOnLoad(value, file.PropertyLocation, pageType); continue; } // Home Page if (homePageRegex.IsMatch(lcIdentifier)) { Path homePageFile = value; string lcExtension = homePageFile.Extension.ToLower(); if (lcExtension != "html" && lcExtension != "htm") { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.HomePageMustHaveHTMLExtension(extension)", homePageFile.Extension)); } else { if (homePageFile.IsRelative) { homePageFile = style.Folder + "/" + homePageFile; } if (!System.IO.File.Exists(homePageFile)) { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.CantFindHomePageFile(name)", homePageFile)); } else if (!style.Folder.Contains(homePageFile)) { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.HomePageFileMustBeInStyleFolder(name, folder)", homePageFile, style.Folder)); } else { style.SetHomePage(homePageFile, file.PropertyLocation); } } continue; } file.AddError(Locale.Get("NaturalDocs.Engine", "ConfigFile.NotAValidIdentifier(identifier)", lcIdentifier)); } // Check for files or JavaScript linked to custom home pages since they wouldn't apply. if (style.HomePage != null) { if (style.Links != null) { foreach (var link in style.Links) { if (link.Type == PageType.Home) { errors.Add(Locale.Get("NaturalDocs.Engine", "Style.txt.CantUseHomeLinksWithCustomHomePage"), link.PropertyLocation); } } } if (style.OnLoad != null) { foreach (var onLoad in style.OnLoad) { if (onLoad.Type == PageType.Home) { errors.Add(Locale.Get("NaturalDocs.Engine", "Style.txt.CantUseHomeOnLoadWithCustomHomePage"), onLoad.PropertyLocation); } } } } if (errorCount == errors.Count) { return(style); } else { return(null); } } }
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); }
/* Function: Load * Loads <Style.txt> and returns it as a <Styles.Advanced> object. If there are any errors found in <Style.txt> they will be added * to the list and the function will return null. */ public Styles.Advanced Load(Path path, Errors.ErrorList errors) { Styles.Advanced style = new Styles.Advanced(path); using (ConfigFile file = new ConfigFile()) { if (!file.Open(path, Config.PropertySource.StyleConfigurationFile, ConfigFile.FileFormatFlags.MakeIdentifiersLowercase | ConfigFile.FileFormatFlags.CondenseIdentifierWhitespace, errors)) { return(null); } int errorCount = errors.Count; string lcIdentifier, value; System.Text.RegularExpressions.Match match; while (file.Get(out lcIdentifier, out value)) { // Inherit if (inheritRegex.IsMatch(lcIdentifier)) { style.AddInheritedStyle(value, file.PropertyLocation, null); continue; } // Link match = linkRegex.Match(lcIdentifier); if (match.Success) { PageType pageType = PageType.All; if (match.Groups[1].Success) { PageType?pageTypeTemp = PageTypes.FromName(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(fullLinkedFile, file.PropertyLocation, pageType); } else { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.CantFindLinkedFile(name)", fullLinkedFile)); } } else { file.AddError(Locale.Get("NaturalDocs.Engine", "Style.txt.CantLinkFileWithExtension(extension)", linkedFile.Extension)); } continue; } // OnLoad match = onLoadRegex.Match(lcIdentifier); if (match.Success) { PageType pageType = PageType.All; if (match.Groups[1].Success) { PageType?pageTypeTemp = PageTypes.FromName(match.Groups[1].ToString()); if (pageTypeTemp == null) { file.AddError(Locale.Get("NaturalDocs.Engine", "ConfigFile.NotAValidIdentifier(identifier)", lcIdentifier)); } else { pageType = pageTypeTemp.Value; } } style.AddOnLoad(value, file.PropertyLocation, pageType); continue; } file.AddError(Locale.Get("NaturalDocs.Engine", "ConfigFile.NotAValidIdentifier(identifier)", lcIdentifier)); } if (errorCount == errors.Count) { return(style); } else { return(null); } } }