diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index cd4abe7f694..bbedae54980 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -76,7 +76,7 @@ public static IEnumerable CompleteCommand(string commandName, } var helper = new PowerShellExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace)); - return CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = helper }, moduleName, commandTypes); + return CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = helper, ExecutionContext = helper.CurrentPowerShell.GetContextFromTLS() }, moduleName, commandTypes); } internal static List CompleteCommand(CompletionContext context) @@ -1325,7 +1325,7 @@ internal static List CompleteCommandArgument(CompletionContext { // For argument completion, we don't want to complete against pseudo commands that only work in the script workflow. // The way to avoid that is to pass in a CompletionContext with RelatedAst = null - var commandResults = CompleteCommand(new CompletionContext { WordToComplete = context.WordToComplete, Helper = context.Helper }); + var commandResults = CompleteCommand(new CompletionContext { WordToComplete = context.WordToComplete, Helper = context.Helper, ExecutionContext = context.ExecutionContext }); if (commandResults != null) result.AddRange(commandResults); } @@ -2088,7 +2088,7 @@ private static void NativeCommandArgumentCompletion( { // For argument completion, we don't want to complete against pseudo commands that only work in the script workflow. // The way to avoid that is to pass in a CompletionContext with RelatedAst = null - var commandResults = CompleteCommand(new CompletionContext { WordToComplete = context.WordToComplete, Helper = context.Helper }); + var commandResults = CompleteCommand(new CompletionContext { WordToComplete = context.WordToComplete, Helper = context.Helper, ExecutionContext = context.ExecutionContext }); if (commandResults != null) result.AddRange(commandResults); } @@ -2778,7 +2778,7 @@ private static void NativeCompletionGetCommand(string commandName, string module RemoveLastNullCompletionResult(result); // Available commands - var commandResults = CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }, moduleName); + var commandResults = CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, moduleName); if (commandResults != null) result.AddRange(commandResults); @@ -2788,7 +2788,7 @@ private static void NativeCompletionGetCommand(string commandName, string module // ps1 files and directories. We only complete the files with .ps1 extension for Get-Command, because the -Syntax // may only works on files with .ps1 extension var ps1Extension = new HashSet(StringComparer.OrdinalIgnoreCase) { StringLiterals.PowerShellScriptFileExtension }; - var moduleFilesResults = new List(CompleteFilename(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }, false, ps1Extension)); + var moduleFilesResults = new List(CompleteFilename(new CompletionContext { WordToComplete = commandName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, false, ps1Extension)); if (moduleFilesResults.Count > 0) result.AddRange(moduleFilesResults); } @@ -2800,7 +2800,7 @@ private static void NativeCompletionGetCommand(string commandName, string module RemoveLastNullCompletionResult(result); var modules = new HashSet(StringComparer.OrdinalIgnoreCase); - var moduleResults = CompleteModuleName(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }, true); + var moduleResults = CompleteModuleName(new CompletionContext { WordToComplete = commandName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, true); if (moduleResults != null) { foreach (CompletionResult moduleResult in moduleResults) @@ -2813,7 +2813,7 @@ private static void NativeCompletionGetCommand(string commandName, string module } } - moduleResults = CompleteModuleName(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }, false); + moduleResults = CompleteModuleName(new CompletionContext { WordToComplete = commandName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, false); if (moduleResults != null) { foreach (CompletionResult moduleResult in moduleResults) @@ -2838,20 +2838,20 @@ private static void NativeCompletionGetHelpCommand(string commandName, string pa // Available commands const CommandTypes commandTypes = CommandTypes.Cmdlet | CommandTypes.Function | CommandTypes.Alias | CommandTypes.ExternalScript | CommandTypes.Workflow | CommandTypes.Configuration; - var commandResults = CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }, null, commandTypes); + var commandResults = CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, null, commandTypes); if (commandResults != null) result.AddRange(commandResults); // ps1 files and directories var ps1Extension = new HashSet(StringComparer.OrdinalIgnoreCase) { StringLiterals.PowerShellScriptFileExtension }; - var fileResults = new List(CompleteFilename(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }, false, ps1Extension)); + var fileResults = new List(CompleteFilename(new CompletionContext { WordToComplete = commandName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, false, ps1Extension)); if (fileResults.Count > 0) result.AddRange(fileResults); if (isHelpRelated) { // Available topics - var helpTopicResults = CompleteHelpTopics(new CompletionContext { WordToComplete = commandName, Helper = context.Helper }); + var helpTopicResults = CompleteHelpTopics(new CompletionContext { WordToComplete = commandName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }); if (helpTopicResults != null) result.AddRange(helpTopicResults); } @@ -3093,7 +3093,7 @@ private static void NativeCompletionModuleCommands(string assemblyOrModuleName, StringLiterals.PowerShellCmdletizationFileExtension, StringLiterals.WorkflowFileExtension }; - var moduleFilesResults = new List(CompleteFilename(new CompletionContext { WordToComplete = assemblyOrModuleName, Helper = context.Helper }, false, moduleExtensions)); + var moduleFilesResults = new List(CompleteFilename(new CompletionContext { WordToComplete = assemblyOrModuleName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, false, moduleExtensions)); if (moduleFilesResults.Count > 0) result.AddRange(moduleFilesResults); @@ -3104,7 +3104,7 @@ private static void NativeCompletionModuleCommands(string assemblyOrModuleName, } } - var moduleResults = CompleteModuleName(new CompletionContext { WordToComplete = assemblyOrModuleName, Helper = context.Helper }, loadedModulesOnly); + var moduleResults = CompleteModuleName(new CompletionContext { WordToComplete = assemblyOrModuleName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, loadedModulesOnly); if (moduleResults != null && moduleResults.Count > 0) result.AddRange(moduleResults); @@ -3115,7 +3115,7 @@ private static void NativeCompletionModuleCommands(string assemblyOrModuleName, RemoveLastNullCompletionResult(result); var moduleExtensions = new HashSet(StringComparer.OrdinalIgnoreCase) { ".dll" }; - var moduleFilesResults = new List(CompleteFilename(new CompletionContext { WordToComplete = assemblyOrModuleName, Helper = context.Helper }, false, moduleExtensions)); + var moduleFilesResults = new List(CompleteFilename(new CompletionContext { WordToComplete = assemblyOrModuleName, Helper = context.Helper, ExecutionContext = context.ExecutionContext }, false, moduleExtensions)); if (moduleFilesResults.Count > 0) result.AddRange(moduleFilesResults); @@ -3490,12 +3490,12 @@ private static void NativeCompletionAliasCommands(string commandName, string par // Complete for the parameter Definition // Available commands const CommandTypes commandTypes = CommandTypes.Cmdlet | CommandTypes.Function | CommandTypes.ExternalScript | CommandTypes.Workflow | CommandTypes.Configuration; - var commandResults = CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = powerShellExecutionHelper }, null, commandTypes); + var commandResults = CompleteCommand(new CompletionContext { WordToComplete = commandName, Helper = powerShellExecutionHelper, ExecutionContext = context.ExecutionContext }, null, commandTypes); if (commandResults != null && commandResults.Count > 0) result.AddRange(commandResults); // The parameter Definition takes a file - var fileResults = new List(CompleteFilename(new CompletionContext { WordToComplete = commandName, Helper = powerShellExecutionHelper })); + var fileResults = new List(CompleteFilename(new CompletionContext { WordToComplete = commandName, Helper = powerShellExecutionHelper, ExecutionContext = context.ExecutionContext })); if (fileResults.Count > 0) result.AddRange(fileResults); } @@ -3597,8 +3597,7 @@ private static void NativeCompletionNewItemCommand(string itemTypeToComplete, st return; } - var powershell = context.Helper.CurrentPowerShell; - var executionContext = powershell.GetContextFromTLS(); + var executionContext = context.ExecutionContext; var boundArgs = GetBoundArgumentsAsHashtable(context); var providedPath = boundArgs["Path"] as string ?? executionContext.SessionState.Path.CurrentLocation.Path; @@ -4035,7 +4034,7 @@ public static IEnumerable CompleteFilename(string fileName) } var helper = new PowerShellExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace)); - return CompleteFilename(new CompletionContext { WordToComplete = fileName, Helper = helper }); + return CompleteFilename(new CompletionContext { WordToComplete = fileName, Helper = helper, ExecutionContext = helper.CurrentPowerShell.GetContextFromTLS() }); } internal static IEnumerable CompleteFilename(CompletionContext context) @@ -4484,7 +4483,7 @@ public static IEnumerable CompleteVariable(string variableName } var helper = new PowerShellExecutionHelper(PowerShell.Create(RunspaceMode.CurrentRunspace)); - return CompleteVariable(new CompletionContext { WordToComplete = variableName, Helper = helper }); + return CompleteVariable(new CompletionContext { WordToComplete = variableName, Helper = helper, ExecutionContext = helper.CurrentPowerShell.GetContextFromTLS() }); } private static readonly string[] s_variableScopes = new string[] { "Global:", "Local:", "Script:", "Private:" }; @@ -5861,7 +5860,7 @@ public static IEnumerable CompleteType(string typeName) : PowerShell.Create(RunspaceMode.CurrentRunspace); var helper = new PowerShellExecutionHelper(powershell); - return CompleteType(new CompletionContext { WordToComplete = typeName, Helper = helper }); + return CompleteType(new CompletionContext { WordToComplete = typeName, Helper = helper, ExecutionContext = helper.CurrentPowerShell.GetContextFromTLS() }); } internal static List CompleteType(CompletionContext context, string prefix = "", string suffix = "") @@ -5952,14 +5951,22 @@ private static string GetNamespaceToRemove(CompletionContext context, TypeComple internal static List CompleteHelpTopics(CompletionContext context) { var results = new List(); - var dirPath = Utils.GetApplicationBase(Utils.DefaultPowerShellShellID) + "\\" + CultureInfo.CurrentCulture.Name; + var dirPath = Utils.GetApplicationBase(Utils.DefaultPowerShellShellID) + Path.DirectorySeparatorChar + CultureInfo.CurrentCulture.Name; var wordToComplete = context.WordToComplete + "*"; var topicPattern = WildcardPattern.Get("about_*.help.txt", WildcardOptions.IgnoreCase); - string[] files = null; + List files = new List(); try { - files = Directory.GetFiles(dirPath, wordToComplete); + var wildcardPattern = WildcardPattern.Get(wordToComplete, WildcardOptions.IgnoreCase); + + foreach(var file in Directory.GetFiles(dirPath)) + { + if(wildcardPattern.IsMatch(Path.GetFileName(file))) + { + files.Add(file); + } + } } catch (Exception) { diff --git a/src/System.Management.Automation/help/MUIFileSearcher.cs b/src/System.Management.Automation/help/MUIFileSearcher.cs index c39aaec247c..bac0b7b30a4 100644 --- a/src/System.Management.Automation/help/MUIFileSearcher.cs +++ b/src/System.Management.Automation/help/MUIFileSearcher.cs @@ -283,7 +283,7 @@ private static Collection NormalizeSearchPaths(string target, Collection } // step 3: locate the file in the default PowerShell installation directory. - string defaultPSPath = GetMshDefaultInstallationPath(); + string defaultPSPath = Utils.GetApplicationBase(Utils.DefaultPowerShellShellID); if (defaultPSPath != null && !result.Contains(defaultPSPath) && Directory.Exists(defaultPSPath)) @@ -294,27 +294,6 @@ private static Collection NormalizeSearchPaths(string target, Collection return result; } - /// - /// Helper method which returns the default monad installation path based on ShellID - /// registry key. - /// - /// string representing path. - /// - /// If ShellID is not defined or Path property is not defined returns null. - /// - private static string GetMshDefaultInstallationPath() - { - string returnValue = CommandDiscovery.GetShellPathFromRegistry(Utils.DefaultPowerShellShellID); - - if (returnValue != null) - { - returnValue = Path.GetDirectoryName(returnValue); - } - - // returnValue can be null. - return returnValue; - } - #endregion #region Static API's diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 6fe7f0ea859..24467867ac3 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -103,6 +103,21 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches[0].CompletionText | Should Be 'namespace' } + It 'Should complete about help topic' { + + $aboutHelpPath = Join-Path $PSHOME (Get-Culture).Name + + ## If help content does not exist, tab completion will not work. So update it first. + if(-not (Test-Path (Join-Path $aboutHelpPath "about_Splatting.help.txt"))) + { + Update-Help -Force -ErrorAction SilentlyContinue + } + + $res = TabExpansion2 -inputScript 'get-help about_spla' -cursorColumn 'get-help about_spla'.Length + $res.CompletionMatches.Count | Should Be 1 + $res.CompletionMatches[0].CompletionText | Should BeExactly 'about_Splatting' + } + Context NativeCommand { BeforeAll { $nativeCommand = (Get-Command -CommandType Application -TotalCount 1).Name diff --git a/test/powershell/engine/Help/HelpSystem.Tests.ps1 b/test/powershell/engine/Help/HelpSystem.Tests.ps1 index af40e77ade5..e229b68dc37 100644 --- a/test/powershell/engine/Help/HelpSystem.Tests.ps1 +++ b/test/powershell/engine/Help/HelpSystem.Tests.ps1 @@ -200,6 +200,12 @@ Describe "Validate about_help.txt under culture specific folder works" -Tags @(' $help.count | Should Be 1 $help | Should BeExactly "Hello" } + + It "Get-Help for about_Variable should return only one help object" { + + $help = Get-Help about_Variables + $help.count | Should Be 1 + } } Describe "Get-Help should find help info within help files" -Tags @('CI', 'RequireAdminOnWindows') {