// 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); }
/* 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); }
/* 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"); }
/* 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); }
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); }
public void AttachScript(string path, string typeName) { var compiler = EngineInstance.GetCompilerService(); EngineInstance.AttachedScriptsFactory.AttachByPath(compiler, path, typeName); }
/* 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); }
/* 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; } } } }
/// <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)); }
/* 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(); } } }
/// <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)); }
/// <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(); }
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); } }
/* 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); }
/* 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); }
// 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); }
public IRuntimeContextInstance LoadScript(string path) { var compiler = EngineInstance.GetCompilerService(); return(EngineInstance.AttachedScriptsFactory.LoadFromPath(compiler, path)); }
/* 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); }
/// <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; }
public void CanLoad() { EngineInstance.EnsureLoaded(); }
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; } }
public void AttachAddIn(string dllPath) { var assembly = System.Reflection.Assembly.LoadFrom(dllPath); EngineInstance.AttachAssembly(assembly, EngineInstance.Environment); }
/* 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"); }