diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs
index d816892bd7..ac8b404835 100644
--- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs
+++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs
@@ -8421,6 +8421,33 @@ namespace System.Management.Automation
}
}
+ ///
+ /// Calls Get-Command to get command info objects.
+ ///
+ /// The fake bound parameters.
+ /// The parameters to add.
+ /// Collection of command info objects.
+ internal static Collection GetCommandInfo(
+ IDictionary fakeBoundParameters,
+ params string[] parametersToAdd)
+ {
+ using var ps = PowerShell.Create(RunspaceMode.CurrentRunspace);
+
+ ps.AddCommand("Get-Command");
+
+ foreach (string parameter in parametersToAdd)
+ {
+ if (fakeBoundParameters.Contains(parameter))
+ {
+ ps.AddParameter(parameter, fakeBoundParameters[parameter]);
+ }
+ }
+
+ Collection commands = ps.Invoke();
+
+ return commands;
+ }
+
internal static bool IsSplattedVariable(Ast targetExpr)
{
if (targetExpr is VariableExpressionAst && ((VariableExpressionAst)targetExpr).Splatted)
diff --git a/src/System.Management.Automation/engine/GetCommandCommand.cs b/src/System.Management.Automation/engine/GetCommandCommand.cs
index a941ce0f2b..9f96c0196e 100644
--- a/src/System.Management.Automation/engine/GetCommandCommand.cs
+++ b/src/System.Management.Automation/engine/GetCommandCommand.cs
@@ -1694,40 +1694,50 @@ namespace Microsoft.PowerShell.Commands
}
///
+ /// Provides argument completion for Noun parameter.
///
public class NounArgumentCompleter : IArgumentCompleter
{
- ///
///
- public IEnumerable CompleteArgument(string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters)
+ /// Returns completion results for Noun parameter.
+ ///
+ /// The command name.
+ /// The parameter name.
+ /// The word to complete.
+ /// The command AST.
+ /// The fake bound parameters.
+ /// List of completion results.
+ public IEnumerable CompleteArgument(
+ string commandName,
+ string parameterName,
+ string wordToComplete,
+ CommandAst commandAst,
+ IDictionary fakeBoundParameters) => CompletionCompleters.GetMatchingResults(
+ wordToComplete,
+ possibleCompletionValues: GetCommandNouns(fakeBoundParameters));
+
+ ///
+ /// Get sorted set of command nouns using Get-Command.
+ ///
+ /// The fake bound parameters.
+ /// Sorted set of command nouns.
+ private static SortedSet GetCommandNouns(IDictionary fakeBoundParameters)
{
- if (fakeBoundParameters == null)
- {
- throw PSTraceSource.NewArgumentNullException(nameof(fakeBoundParameters));
- }
+ Collection commands = CompletionCompleters.GetCommandInfo(fakeBoundParameters, "Module", "Verb");
+ SortedSet nouns = new(StringComparer.OrdinalIgnoreCase);
- var commandInfo = new CmdletInfo("Get-Command", typeof(GetCommandCommand));
- var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)
- .AddCommand(commandInfo)
- .AddParameter("Noun", wordToComplete + "*");
-
- if (fakeBoundParameters.Contains("Module"))
+ foreach (CommandInfo command in commands)
{
- ps.AddParameter("Module", fakeBoundParameters["Module"]);
- }
-
- HashSet nouns = new HashSet();
- var results = ps.Invoke();
- foreach (var result in results)
- {
- var dash = result.Name.IndexOf('-');
- if (dash != -1)
+ string commandName = command.Name;
+ int dashIndex = commandName.IndexOf('-');
+ if (dashIndex != -1)
{
- nouns.Add(result.Name.Substring(dash + 1));
+ string noun = commandName.Substring(dashIndex + 1);
+ nouns.Add(noun);
}
}
- return nouns.Order().Select(static noun => new CompletionResult(noun, noun, CompletionResultType.Text, noun));
+ return nouns;
}
}
}
diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
index 049c909fad..5bb67a3ab2 100644
--- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
+++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1
@@ -1078,6 +1078,86 @@ ConstructorTestClass(int i, bool b)
}
}
+ Context 'Get-Command -Noun parameter completion' {
+ BeforeAll {
+ function GetModuleCommandNouns(
+ [string]$Module,
+ [string]$Verb,
+ [switch]$SingleQuote,
+ [switch]$DoubleQuote)
+ {
+
+ $commandParams = @{}
+
+ if ($PSBoundParameters.ContainsKey('Module')) {
+ $commandParams['Module'] = $Module
+ }
+
+ if ($PSBoundParameters.ContainsKey('Verb')) {
+ $commandParams['Verb'] = $Verb
+ }
+
+ $nouns = (Get-Command @commandParams).Noun
+
+ if ($SingleQuote) {
+ return ($nouns | ForEach-Object { "'$_'" })
+ }
+ elseif ($DoubleQuote) {
+ return ($nouns | ForEach-Object { """$_""" })
+ }
+
+ return $nouns
+ }
+
+ $utilityModuleName = 'Microsoft.PowerShell.Utility'
+
+ $allUtilityCommandNouns = GetModuleCommandNouns -Module $utilityModuleName
+ $allUtilityCommandNounsSingleQuote = GetModuleCommandNouns -Module $utilityModuleName -SingleQuote
+ $allUtilityCommandNounsDoubleQuote = GetModuleCommandNouns -Module $utilityModuleName -DoubleQuote
+ $utilityCommandNounsStartingWithF = $allUtilityCommandNouns | Where-Object { $_ -like 'F*'}
+ $utilityCommandNounsStartingWithFSingleQuote = $allUtilityCommandNounsSingleQuote | Where-Object { $_ -like "'F*"}
+ $utilityCommandNounsStartingWithFDoubleQuote = $allUtilityCommandNounsDoubleQuote | Where-Object { $_ -like """F*"}
+
+ $allUtilityCommandNounsWithConvertToVerb = GetModuleCommandNouns -Module $utilityModuleName -Verb 'ConvertTo'
+ $allUtilityCommandNounsWithConvertToVerbSingleQuote = GetModuleCommandNouns -Module $utilityModuleName -SingleQuote -Verb 'ConvertTo'
+ $allUtilityCommandNounsWithConvertToVerbDoubleQuote = GetModuleCommandNouns -Module $utilityModuleName -DoubleQuote -Verb 'ConvertTo'
+ $utilityCommandNounsWithConvertToVerb = $allUtilityCommandNounsWithConvertToVerb | Where-Object { $_ -in 'CliXml', 'Csv', 'Html', 'Json', 'Xml' }
+ $utilityCommandNounsWithConvertToVerbSingleQuote = $allUtilityCommandNounsWithConvertToVerbSingleQuote | Where-Object { $_ -in "'CliXml'", "'Csv'", "'Html'", "'Json'", "'Xml'" }
+ $utilityCommandNounsWithConvertToVerbDoubleQuote = $allUtilityCommandNounsWithConvertToVerbDoubleQuote | Where-Object { $_ -in """CliXml""", """Csv""", """Html""", """Json""", """Xml""" }
+ $utilityCommandNounsWithConvertToVerbStartingWithC = $allUtilityCommandNounsWithConvertToVerb | Where-Object { $_ -in 'CliXml', 'Csv' }
+ $utilityCommandNounsWithConvertToVerbStartingWithCSingleQuote = $allUtilityCommandNounsWithConvertToVerbSingleQuote | Where-Object { $_ -in "'CliXml'", "'Csv'" }
+ $utilityCommandNounsWithConvertToVerbStartingWithCDoubleQuote = $allUtilityCommandNounsWithConvertToVerbDoubleQuote | Where-Object { $_ -in """CliXml""", """Csv""" }
+ }
+
+ It "Should complete Noun for ''" -TestCases @(
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Noun "; ExpectedNouns = $allUtilityCommandNouns }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Noun '"; ExpectedNouns = $allUtilityCommandNounsSingleQuote }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Noun """; ExpectedNouns = $allUtilityCommandNounsDoubleQuote }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Noun F"; ExpectedNouns = $utilityCommandNounsStartingWithF }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Noun 'F"; ExpectedNouns = $utilityCommandNounsStartingWithFSingleQuote }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Noun ""F"; ExpectedNouns = $utilityCommandNounsStartingWithFDoubleQuote }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Verb ConvertTo -Noun "; ExpectedNouns = $utilityCommandNounsWithConvertToVerb }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Verb ConvertTo -Noun '"; ExpectedNouns = $utilityCommandNounsWithConvertToVerbSingleQuote }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Verb ConvertTo -Noun """; ExpectedNouns = $utilityCommandNounsWithConvertToVerbDoubleQuote }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Verb ConvertTo -Noun C"; ExpectedNouns = $utilityCommandNounsWithConvertToVerbStartingWithC }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Verb ConvertTo -Noun 'C"; ExpectedNouns = $utilityCommandNounsWithConvertToVerbStartingWithCSingleQuote }
+ @{ TextInput = "Get-Command -Module $utilityModuleName -Verb ConvertTo -Noun ""C"; ExpectedNouns = $utilityCommandNounsWithConvertToVerbStartingWithCDoubleQuote }
+ ) {
+ param($TextInput, $ExpectedNouns)
+ $res = TabExpansion2 -inputScript $TextInput -cursorColumn $TextInput.Length
+ $completionText = $res.CompletionMatches.CompletionText
+
+ # Avoid using Sort-Object -Unique because it generates different order than SortedSet on MacOS/Linux
+ $sortedSetExpectedNouns = [System.Collections.Generic.SortedSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
+ foreach ($noun in $ExpectedNouns)
+ {
+ $sortedSetExpectedNouns.Add($noun) | Out-Null
+ }
+
+ $completionText -join ' ' | Should -BeExactly ($sortedSetExpectedNouns -join ' ')
+ }
+ }
+
Context "Format cmdlet's View paramter completion" {
BeforeAll {
$viewDefinition = @'