//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="cache">The cache with which this indexed document is associated</param> /// <param name="filename">The name of the XML comments file to index</param> public IndexedCommentsFile(IndexedCommentsCache cache, string filename) { foreach (var kv in cache.GetValues(filename)) { index[kv.Key] = kv.Value; } }
/// <summary> /// This is called if indexing completes successfully /// </summary> /// <param name="cache">The index cache</param> private void IndexCompleted(IndexedCommentsCache cache) { this.DisposeOfTask(); spIndexingPanel.Visibility = Visibility.Collapsed; codeEntities = new List<string>(cache.AllKeys); if(cboEntityType.SelectedIndex == (int)EntityType.CodeEntity) { txtFindName.IsReadOnly = false; txtFindName.Text = "<Enter text or reg ex to find here, then click Go>"; txtFindName.SelectAll(); tvEntities.IsEnabled = btnGo.IsEnabled = true; } }
/// <summary> /// This is the method that indexes the comments files /// </summary> /// <remarks>Rather than a partial build, we'll just index the comments files.</remarks> private IndexedCommentsCache IndexComments() { HashSet<string> projectDictionary = new HashSet<string>(); IndexedCommentsCache cache = new IndexedCommentsCache(100); MSBuildProject projRef; string lastSolution = null; // Index the framework comments based on the framework version in the project FrameworkSettings frameworkSettings = FrameworkDictionary.AllFrameworks.GetFrameworkWithRedirect( currentProject.FrameworkVersion); if(frameworkSettings == null) throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Unable to locate information for a framework version or its redirect: {0}", currentProject.FrameworkVersion)); foreach(var location in frameworkSettings.CommentsFileLocations(currentProject.Language)) { indexTokenSource.Token.ThrowIfCancellationRequested(); cache.IndexCommentsFiles(Path.GetDirectoryName(location), Path.GetFileName(location), false, null); } // Index the comments file documentation sources foreach(string file in currentProject.DocumentationSources.SelectMany(ds => ds.CommentsFiles)) { indexTokenSource.Token.ThrowIfCancellationRequested(); cache.IndexCommentsFiles(Path.GetDirectoryName(file), Path.GetFileName(file), false, null); } // Also, index the comments files in project documentation sources foreach(DocumentationSource ds in currentProject.DocumentationSources) foreach(var sourceProject in ds.Projects( !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration, !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform)) { indexTokenSource.Token.ThrowIfCancellationRequested(); // NOTE: This code should be similar to the code in BuildProcess.ValidateDocumentationSources! // Solutions are followed by the projects that they contain if(sourceProject.ProjectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) { lastSolution = sourceProject.ProjectFileName; continue; } // Ignore projects that we've already seen if(projectDictionary.Add(sourceProject.ProjectFileName)) { using(projRef = new MSBuildProject(sourceProject.ProjectFileName)) { // Use the project file configuration and platform properties if they are set. If // not, use the documentation source values. If they are not set, use the SHFB // project settings. projRef.SetConfiguration( !String.IsNullOrEmpty(sourceProject.Configuration) ? sourceProject.Configuration : !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration, !String.IsNullOrEmpty(sourceProject.Platform) ? sourceProject.Platform : !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform, currentProject.MSBuildOutDir, false); // Add Visual Studio solution macros if necessary if(lastSolution != null) projRef.SetSolutionMacros(lastSolution); if(!String.IsNullOrEmpty(projRef.XmlCommentsFile)) cache.IndexCommentsFiles(Path.GetDirectoryName(projRef.XmlCommentsFile), Path.GetFileName(projRef.XmlCommentsFile), false, null); } } } return cache; }
/// <summary> /// This is called if indexing completes successfully /// </summary> /// <param name="cache">The index cache</param> private void IndexCompleted(IndexedCommentsCache cache) { this.DisposeOfTask(); spIndexingPanel.Visibility = Visibility.Collapsed; codeEntities = new List<string>(cache.AllKeys); // Add an entry for the root namespace container codeEntities.Add("R:Project_" + currentProject.HtmlHelpName.Replace(" ", "_")); if(cboEntityType.SelectedIndex == (int)EntityType.CodeEntity) { txtFindName.IsReadOnly = false; txtFindName.Text = "<Enter text or reg ex to find here, then click Go>"; txtFindName.SelectAll(); tvEntities.IsEnabled = btnGo.IsEnabled = true; } }
//===================================================================== /// <summary> /// Constructor /// </summary> /// <param name="cache">The cache with which this indexed document is associated</param> /// <param name="filename">The name of the XML comments file to index</param> public IndexedCommentsFile(IndexedCommentsCache cache, string filename) { foreach(var kv in cache.GetValues(filename)) index[kv.Key] = kv.Value; }
//===================================================================== /// <summary> /// Load the configuration file /// </summary> /// <param name="configFile">The configuration file to load</param> /// <exception cref="ArgumentException">This is thrown if the /// configuration file does not exist.</exception> /// <exception cref="ConfigurationErrorsException">This is thrown if /// the configuration file is not valid.</exception> private static void LoadConfiguration(string configFile) { XPathDocument config, xpathDoc; XPathNavigator navConfig, navComments, element, refInfo; string path, wildcard, attrValue; bool recurse; int cacheSize = 100; if(!File.Exists(configFile)) throw new ArgumentException("Configuration file not found: " + configFile, "configFile"); config = new XPathDocument(configFile); navConfig = config.CreateNavigator(); // Show duplicate key warnings? showDupWarning = (navConfig.SelectSingleNode("configuration/showDuplicateWarning") != null); // Get the reflection information file element = navConfig.SelectSingleNode("configuration/reflectionInfo/@file"); if(element == null || !File.Exists(element.Value)) throw new ConfigurationErrorsException("The reflectionFile " + "element is missing or the specified file does not exist"); Console.WriteLine("Reflection information will be retrieved from '{0}'", element.Value); xpathDoc = new XPathDocument(element.Value); refInfo = xpathDoc.CreateNavigator(); apisNode = refInfo.SelectSingleNode("reflection/apis"); // Get the inherited documentation filename element = navConfig.SelectSingleNode("configuration/inheritedDocs/@file"); if(element == null) throw new ConfigurationErrorsException("The inheritedDocs " + "element does not exist or is not valid"); inheritedDocsFilename = element.Value; Console.WriteLine("Inherited documentation will be written to '{0}'", inheritedDocsFilename); // Load the comments file information navComments = navConfig.SelectSingleNode("configuration/commentsFiles"); attrValue = navComments.GetAttribute("cacheSize", String.Empty); if(attrValue.Length != 0) cacheSize = Convert.ToInt32(attrValue, CultureInfo.InvariantCulture); commentsCache = new IndexedCommentsCache(cacheSize); commentsCache.ReportWarning += commentsCache_ReportWarning; foreach(XPathNavigator nav in navComments.Select("*")) { path = nav.GetAttribute("path", String.Empty); wildcard = nav.GetAttribute("file", String.Empty); attrValue = nav.GetAttribute("recurse", String.Empty); if(path.Length == 0) { path = Path.GetDirectoryName(wildcard); wildcard = Path.GetFileName(wildcard); } path = Environment.ExpandEnvironmentVariables(path); if(wildcard.Length != 0) wildcard = Environment.ExpandEnvironmentVariables(wildcard); else wildcard = "*.xml"; if(attrValue.Length == 0) recurse = false; else recurse = Convert.ToBoolean(attrValue, CultureInfo.InvariantCulture); Console.WriteLine("Indexing {0}\\{1}", path, wildcard); commentsCache.IndexCommentsFiles(path, wildcard, recurse, (nav.Name == "scan") ? commentsFiles : null); } if(commentsCache.FilesIndexed == 0 || commentsCache.IndexCount == 0) throw new ConfigurationErrorsException("No comments files were specified or they did not " + "contain valid information to index"); if(commentsFiles.Count == 0) throw new ConfigurationErrorsException("No comments files were specified to scan for " + "<inheritdoc /> tags."); Console.WriteLine("Indexed {0} members in {1} file(s). {2} file(s) to scan for <inheritdoc /> tags.", commentsCache.IndexCount, commentsCache.FilesIndexed, commentsFiles.Count); }
//===================================================================== /// <summary> /// Load the configuration file /// </summary> /// <param name="configFile">The configuration file to load</param> /// <exception cref="ArgumentException">This is thrown if the configuration file does not exist or is not valid</exception> private void LoadConfiguration(string configFile) { XPathDocument config; XPathNavigator navConfig, navComments, element; string path, wildcard, attrValue; bool recurse; int cacheSize = 100; if (!File.Exists(configFile)) { throw new ArgumentException("Configuration file not found: " + configFile, nameof(configFile)); } using (var reader = XmlReader.Create(configFile, new XmlReaderSettings { CloseInput = true })) { config = new XPathDocument(reader); navConfig = config.CreateNavigator(); // Show duplicate key warnings? showDupWarning = (navConfig.SelectSingleNode("configuration/showDuplicateWarning") != null); // Get the reflection information files reflectionFiles = new ReflectionFiles(); foreach (XPathNavigator refFile in navConfig.Select("configuration/reflectionInfo/@file")) { if (!File.Exists(refFile.Value)) { throw new ArgumentException("The reflectionFile element's target file '" + refFile.Value + "' does not exist", nameof(configFile)); } this.Log.LogMessage("Reflection information will be retrieved from '{0}'", refFile.Value); reflectionFiles.AddReflectionFile(refFile.Value); } if (reflectionFiles.Count == 0) { throw new ArgumentException("At least one reflectionFile element is required", nameof(configFile)); } // Get the inherited documentation filename element = navConfig.SelectSingleNode("configuration/inheritedDocs/@file"); if (element == null) { throw new ArgumentException("The inheritedDocs element does not exist or is not valid", nameof(configFile)); } inheritedDocsFilename = element.Value; this.Log.LogMessage("Inherited documentation will be written to '{0}'", inheritedDocsFilename); // Load the comments file information navComments = navConfig.SelectSingleNode("configuration/commentsFiles"); attrValue = navComments.GetAttribute("cacheSize", String.Empty); if (attrValue.Length != 0) { cacheSize = Convert.ToInt32(attrValue, CultureInfo.InvariantCulture); } commentsCache = new IndexedCommentsCache(cacheSize) { ShowDuplicatesWarning = showDupWarning }; commentsCache.ReportWarning += (s, e) => this.Log.LogMessage(e.Message); foreach (XPathNavigator nav in navComments.Select("*")) { path = nav.GetAttribute("path", String.Empty); wildcard = nav.GetAttribute("file", String.Empty); attrValue = nav.GetAttribute("recurse", String.Empty); if (path.Length == 0) { path = Path.GetDirectoryName(wildcard); wildcard = Path.GetFileName(wildcard); } path = Environment.ExpandEnvironmentVariables(path); if (wildcard.Length != 0) { wildcard = Environment.ExpandEnvironmentVariables(wildcard); } else { wildcard = "*.xml"; } if (attrValue.Length == 0) { recurse = false; } else { recurse = Convert.ToBoolean(attrValue, CultureInfo.InvariantCulture); } this.Log.LogMessage("Indexing {0}\\{1}", path, wildcard); commentsCache.IndexCommentsFiles(path, wildcard, recurse, (nav.Name == "scan") ? commentsFiles : null); } } if (commentsCache.FilesIndexed == 0 || commentsCache.IndexCount == 0) { throw new ArgumentException("No comments files were specified or they did not " + "contain valid information to index", nameof(configFile)); } if (commentsFiles.IsEmpty) { throw new ArgumentException("No comments files were specified to scan for " + "<inheritdoc /> tags.", nameof(configFile)); } this.Log.LogMessage("Indexed {0} members in {1} file(s). {2} file(s) to scan for <inheritdoc /> tags.", commentsCache.IndexCount, commentsCache.FilesIndexed, commentsFiles.Count); }
/// <summary> /// This is the thread method that indexes the comments files /// </summary> /// <remarks>Rather than a partial build, we'll just index the /// comments files.</remarks> private void IndexComments() { HashSet<string> projectDictionary = new HashSet<string>(); Collection<string> frameworkLocations = new Collection<string>(); Dictionary<string, string> cacheName = new Dictionary<string,string>(); IndexedCommentsCache cache = new IndexedCommentsCache(100); MSBuildProject projRef; string path, lastSolution = null; try { BuildProcess.GetFrameworkCommentsFiles(frameworkLocations, cacheName, currentProject.Language, currentProject.FrameworkVersion); // Index the framework comments foreach(string location in frameworkLocations) { path = Environment.ExpandEnvironmentVariables(location); cache.IndexCommentsFiles(path, null, true, null); } // Index the comments file documentation sources foreach(string file in currentProject.DocumentationSources.CommentsFiles) cache.IndexCommentsFiles(Path.GetDirectoryName(file), Path.GetFileName(file), false, null); // Also, index the comments files in project documentation sources foreach(DocumentationSource ds in currentProject.DocumentationSources) foreach(var sourceProject in DocumentationSource.Projects(ds.SourceFile, ds.IncludeSubFolders, !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration, !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform)) { // NOTE: This code should be similar to the code in BuildProcess.ValidateDocumentationSources! // Solutions are followed by the projects that they contain if(sourceProject.ProjectFileName.EndsWith(".sln", StringComparison.OrdinalIgnoreCase)) { lastSolution = sourceProject.ProjectFileName; continue; } // Ignore projects that we've already seen if(projectDictionary.Add(sourceProject.ProjectFileName)) { projRef = new MSBuildProject(sourceProject.ProjectFileName); // Use the project file configuration and platform properties if they are set. If not, // use the documentation source values. If they are not set, use the SHFB project settings. projRef.SetConfiguration( !String.IsNullOrEmpty(sourceProject.Configuration) ? sourceProject.Configuration : !String.IsNullOrEmpty(ds.Configuration) ? ds.Configuration : currentProject.Configuration, !String.IsNullOrEmpty(sourceProject.Platform) ? sourceProject.Platform : !String.IsNullOrEmpty(ds.Platform) ? ds.Platform : currentProject.Platform, currentProject.MSBuildOutDir); // Add Visual Studio solution macros if necessary if(lastSolution != null) projRef.SetSolutionMacros(lastSolution); if(!String.IsNullOrEmpty(projRef.XmlCommentsFile)) cache.IndexCommentsFiles(Path.GetDirectoryName(projRef.XmlCommentsFile), Path.GetFileName(projRef.XmlCommentsFile), false, null); } } this.Invoke(new IndexingCompleted(this.Completed), new object[] { cache }); } catch(Exception ex) { System.Diagnostics.Debug.WriteLine(ex.ToString()); this.Invoke(new IndexingFailed(this.Failed), new object[] { ex.Message }); } }
/// <summary> /// This is called if indexing completes successfully /// </summary> private void Completed(IndexedCommentsCache cache) { indexThread = null; pbWait.Visible = lblLoading.Visible = false; tvEntities.Enabled = true; codeEntities = cache.GetKeys(); if(cboContentType.SelectedIndex == (int)EntityType.CodeEntities) { txtFindName.Enabled = true; txtFindName.ReadOnly = false; } }