/
githubmirror
/
PowerShell
Обзор
Документация
Войти
/
githubmirror
/
PowerShell
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/System.Management.Automation/help/PSClassHelpProvider.cs
371 строка
13 KB
Shuangchi He
Fix some typos in source code (#17481)
06 июн 2022, 20:14
Не верифицирован
06 июн 2022, 20:14
8cc3984
Код
Авторство
О чём код?
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Xml; using Dbg = System.Management.Automation.Diagnostics; namespace System.Management.Automation { internal class PSClassHelpProvider : HelpProviderWithCache { /// <summary> /// Constructor for PSClassHelpProvider. /// </summary> internal PSClassHelpProvider(HelpSystem helpSystem) : base(helpSystem) { _context = helpSystem.ExecutionContext; } /// <summary> /// Execution context of the HelpSystem. /// </summary> private readonly ExecutionContext _context; /// <summary> /// This is a hashtable to track which help files are loaded already. /// /// This will avoid one help file getting loaded again and again. /// </summary> private readonly Hashtable _helpFiles = new Hashtable(); [TraceSource("PSClassHelpProvider", "PSClassHelpProvider")] private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("PSClassHelpProvider", "PSClassHelpProvider"); #region common properties /// <summary> /// Name of the Help Provider. /// </summary> internal override string Name { get { return "Powershell Class Help Provider"; } } /// <summary> /// Supported Help Categories. /// </summary> internal override HelpCategory HelpCategory { get { return Automation.HelpCategory.Class; } } #endregion /// <summary> /// Override SearchHelp to find a class module with help matching a pattern. /// </summary> /// <param name="helpRequest">Help request.</param> /// <param name="searchOnlyContent">Not used.</param> /// <returns></returns> internal override IEnumerable<HelpInfo> SearchHelp(HelpRequest helpRequest, bool searchOnlyContent) { Debug.Assert(helpRequest != null, "helpRequest cannot be null."); string target = helpRequest.Target; Collection<string> patternList = new Collection<string>(); bool decoratedSearch = !WildcardPattern.ContainsWildcardCharacters(helpRequest.Target); if (decoratedSearch) { patternList.Add("*" + target + "*"); } else patternList.Add(target); foreach (string pattern in patternList) { PSClassSearcher searcher = new PSClassSearcher(pattern, useWildCards: true, _context); foreach (var helpInfo in GetHelpInfo(searcher)) { if (helpInfo != null) yield return helpInfo; } } } /// <summary> /// Override ExactMatchHelp to find the matching class module matching help request. /// </summary> /// <param name="helpRequest">Help Request for the search.</param> /// <returns>Enumerable of HelpInfo objects.</returns> internal override IEnumerable<HelpInfo> ExactMatchHelp(HelpRequest helpRequest) { Debug.Assert(helpRequest != null, "helpRequest cannot be null."); if ((helpRequest.HelpCategory & Automation.HelpCategory.Class) == 0) { yield return null; } PSClassSearcher searcher = new PSClassSearcher(helpRequest.Target, useWildCards: false, _context); foreach (var helpInfo in GetHelpInfo(searcher)) { if (helpInfo != null) { yield return helpInfo; } } } /// <summary> /// Get the help in for the PS Class Info. /// /// </summary> /// <param name="searcher">Searcher for PS Classes.</param> /// <returns>Next HelpInfo object.</returns> private IEnumerable<HelpInfo> GetHelpInfo(PSClassSearcher searcher) { while (searcher.MoveNext()) { PSClassInfo current = ((IEnumerator<PSClassInfo>)searcher).Current; string moduleName = current.Module.Name; string moduleDir = current.Module.ModuleBase; if (!string.IsNullOrEmpty(moduleName) && !string.IsNullOrEmpty(moduleDir)) { string helpFileToFind = moduleName + "-Help.xml"; string helpFileName = null; Collection<string> searchPaths = new Collection<string>(); searchPaths.Add(moduleDir); string externalHelpFile = current.HelpFile; if (!string.IsNullOrEmpty(externalHelpFile)) { FileInfo helpFileInfo = new FileInfo(externalHelpFile); DirectoryInfo dirToSearch = helpFileInfo.Directory; if (dirToSearch.Exists) { searchPaths.Add(dirToSearch.FullName); helpFileToFind = helpFileInfo.Name; // If external help file is specified. Then use it. } } HelpInfo helpInfo = GetHelpInfoFromHelpFile(current, helpFileToFind, searchPaths, true, out helpFileName); if (helpInfo != null) { yield return helpInfo; } } } } /// <summary> /// Check whether a HelpItems node indicates that the help content is /// authored using maml schema. /// /// This covers two cases: /// a. If the help file has an extension .maml. /// b. If HelpItems node (which should be the top node of any command help file) /// has an attribute "schema" with value "maml", its content is in maml /// schema. /// </summary> /// <param name="helpFile">File name.</param> /// <param name="helpItemsNode">Nodes to check.</param> /// <returns></returns> internal static bool IsMamlHelp(string helpFile, XmlNode helpItemsNode) { Debug.Assert(!string.IsNullOrEmpty(helpFile), "helpFile cannot be null."); if (helpFile.EndsWith(".maml", StringComparison.OrdinalIgnoreCase)) return true; if (helpItemsNode.Attributes == null) return false; foreach (XmlNode attribute in helpItemsNode.Attributes) { if (attribute.Name.Equals("schema", StringComparison.OrdinalIgnoreCase) && attribute.Value.Equals("maml", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } #region private methods private HelpInfo GetHelpInfoFromHelpFile(PSClassInfo classInfo, string helpFileToFind, Collection<string> searchPaths, bool reportErrors, out string helpFile) { Dbg.Assert(classInfo != null, "Caller should verify that classInfo != null"); Dbg.Assert(helpFileToFind != null, "Caller should verify that helpFileToFind != null"); helpFile = MUIFileSearcher.LocateFile(helpFileToFind, searchPaths); if (!File.Exists(helpFile)) return null; if (!string.IsNullOrEmpty(helpFile)) { // Load the help file only once. Then use it from the cache. if (!_helpFiles.Contains(helpFile)) { LoadHelpFile(helpFile, helpFile, classInfo.Name, reportErrors); } return GetFromPSClassHelpCache(helpFile, Automation.HelpCategory.Class); } return null; } /// <summary> /// Gets the HelpInfo object corresponding to the command. /// </summary> /// <param name="helpFileIdentifier">Help file identifier (either name of PSSnapIn or simply full path to help file).</param> /// <param name="helpCategory">Help Category for search.</param> /// <returns>HelpInfo object.</returns> private HelpInfo GetFromPSClassHelpCache(string helpFileIdentifier, HelpCategory helpCategory) { Debug.Assert(!string.IsNullOrEmpty(helpFileIdentifier), "helpFileIdentifier should not be null or empty."); HelpInfo result = GetCache(helpFileIdentifier); if (result != null) { MamlClassHelpInfo original = (MamlClassHelpInfo)result; result = original.Copy(helpCategory); } return result; } private void LoadHelpFile(string helpFile, string helpFileIdentifier, string commandName, bool reportErrors) { Exception e = null; try { LoadHelpFile(helpFile, helpFileIdentifier); } catch (IOException ioException) { e = ioException; } catch (System.Security.SecurityException securityException) { e = securityException; } catch (XmlException xmlException) { e = xmlException; } catch (NotSupportedException notSupportedException) { e = notSupportedException; } catch (UnauthorizedAccessException unauthorizedAccessException) { e = unauthorizedAccessException; } catch (InvalidOperationException invalidOperationException) { e = invalidOperationException; } if (e != null) s_tracer.WriteLine("Error occurred in PSClassHelpProvider {0}", e.Message); if (reportErrors && (e != null)) { ReportHelpFileError(e, commandName, helpFile); } } /// <summary> /// Load help file for HelpInfo objects. The HelpInfo objects will be /// put into help cache. /// </summary> /// <remarks> /// 1. Needs to pay special attention about error handling in this function. /// Common errors include: file not found and invalid xml. None of these error /// should cause help search to stop. /// 2. a helpfile cache is used to avoid same file got loaded again and again. /// </remarks> private void LoadHelpFile(string helpFile, string helpFileIdentifier) { Dbg.Assert(!string.IsNullOrEmpty(helpFile), "HelpFile cannot be null or empty."); Dbg.Assert(!string.IsNullOrEmpty(helpFileIdentifier), "helpFileIdentifier cannot be null or empty."); XmlDocument doc = InternalDeserializer.LoadUnsafeXmlDocument( new FileInfo(helpFile), false, /* ignore whitespace, comments, etc. */ null); /* default maxCharactersInDocument */ // Add this file into _helpFiles hashtable to prevent it to be loaded again. _helpFiles[helpFile] = 0; XmlNode helpItemsNode = null; if (doc.HasChildNodes) { for (int i = 0; i < doc.ChildNodes.Count; i++) { XmlNode node = doc.ChildNodes[i]; if (node.NodeType == XmlNodeType.Element && string.Equals(node.LocalName, "helpItems", StringComparison.OrdinalIgnoreCase)) { helpItemsNode = node; break; } } } if (helpItemsNode == null) { s_tracer.WriteLine("Unable to find 'helpItems' element in file {0}", helpFile); return; } bool isMaml = IsMamlHelp(helpFile, helpItemsNode); using (this.HelpSystem.Trace(helpFile)) { if (helpItemsNode.HasChildNodes) { for (int i = 0; i < helpItemsNode.ChildNodes.Count; i++) { XmlNode node = helpItemsNode.ChildNodes[i]; string nodeLocalName = node.LocalName; bool isClass = (string.Equals(nodeLocalName, "class", StringComparison.OrdinalIgnoreCase)); if (node.NodeType == XmlNodeType.Element && isClass) { MamlClassHelpInfo helpInfo = null; if (isMaml) { if (isClass) helpInfo = MamlClassHelpInfo.Load(node, HelpCategory.Class); } if (helpInfo != null) { this.HelpSystem.TraceErrors(helpInfo.Errors); AddCache(helpFileIdentifier, helpInfo); } } } } } } #endregion } }