diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index b9f2ef70db..33911cfcee 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -1658,9 +1658,9 @@ namespace System.Management.Automation /// /// /// If the argument completion falls into these pre-defined cases: - /// 1. The matching parameter is of type Enum - /// 2. The matching parameter is of type SwitchParameter - /// 3. The matching parameter is declared with ValidateSetAttribute + /// 1. The matching parameter is declared with ValidateSetAttribute + /// 2. The matching parameter is of type Enum + /// 3. The matching parameter is of type SwitchParameter /// 4. Falls into the native command argument completion /// a null instance of CompletionResult is added to the end of the /// "result" list, to indicate that this particular argument completion @@ -1683,6 +1683,74 @@ namespace System.Management.Automation parameterType = parameterType.GetElementType(); } + foreach (ValidateArgumentsAttribute att in parameter.Parameter.ValidationAttributes) + { + if (att is ValidateSetAttribute setAtt) + { + RemoveLastNullCompletionResult(result); + + string wordToComplete = context.WordToComplete; + string quote = HandleDoubleAndSingleQuote(ref wordToComplete); + + var pattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); + var setList = new List(); + + foreach (string value in setAtt.ValidValues) + { + if (value == string.Empty) + { + continue; + } + + if (wordToComplete.Equals(value, StringComparison.OrdinalIgnoreCase)) + { + string completionText = quote == string.Empty ? value : quote + value + quote; + fullMatch = new CompletionResult(completionText, value, CompletionResultType.ParameterValue, value); + continue; + } + + if (pattern.IsMatch(value)) + { + setList.Add(value); + } + } + + if (fullMatch != null) + { + result.Add(fullMatch); + } + + setList.Sort(); + foreach (string entry in setList) + { + string realEntry = entry; + string completionText = entry; + if (quote == string.Empty) + { + if (CompletionRequiresQuotes(entry, false)) + { + realEntry = CodeGeneration.EscapeSingleQuotedStringContent(entry); + completionText = "'" + realEntry + "'"; + } + } + else + { + if (quote.Equals("'", StringComparison.OrdinalIgnoreCase)) + { + realEntry = CodeGeneration.EscapeSingleQuotedStringContent(entry); + } + + completionText = quote + realEntry + quote; + } + + result.Add(new CompletionResult(completionText, entry, CompletionResultType.ParameterValue, entry)); + } + + result.Add(CompletionResult.Null); + return; + } + } + if (parameterType.IsEnum) { RemoveLastNullCompletionResult(result); @@ -1740,71 +1808,6 @@ namespace System.Management.Automation return; } - foreach (ValidateArgumentsAttribute att in parameter.Parameter.ValidationAttributes) - { - if (att is ValidateSetAttribute setAtt) - { - RemoveLastNullCompletionResult(result); - - string wordToComplete = context.WordToComplete; - string quote = HandleDoubleAndSingleQuote(ref wordToComplete); - - var pattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); - var setList = new List(); - - foreach (string value in setAtt.ValidValues) - { - if (value == string.Empty) { continue; } - - if (wordToComplete.Equals(value, StringComparison.OrdinalIgnoreCase)) - { - string completionText = quote == string.Empty ? value : quote + value + quote; - fullMatch = new CompletionResult(completionText, value, CompletionResultType.ParameterValue, value); - continue; - } - - if (pattern.IsMatch(value)) - { - setList.Add(value); - } - } - - if (fullMatch != null) - { - result.Add(fullMatch); - } - - setList.Sort(); - foreach (string entry in setList) - { - string realEntry = entry; - string completionText = entry; - if (quote == string.Empty) - { - if (CompletionRequiresQuotes(entry, false)) - { - realEntry = CodeGeneration.EscapeSingleQuotedStringContent(entry); - completionText = "'" + realEntry + "'"; - } - } - else - { - if (quote.Equals("'", StringComparison.OrdinalIgnoreCase)) - { - realEntry = CodeGeneration.EscapeSingleQuotedStringContent(entry); - } - - completionText = quote + realEntry + quote; - } - - result.Add(new CompletionResult(completionText, entry, CompletionResultType.ParameterValue, entry)); - } - - result.Add(CompletionResult.Null); - return; - } - } - NativeCommandArgumentCompletion(commandName, parameter.Parameter, result, commandAst, context, boundArguments); } diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 31d1aa5d10..b5194cfe7d 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -1001,6 +1001,15 @@ Describe "TabCompletion" -Tags CI { $res.CompletionMatches[1].CompletionText | Should -BeExactly 'dog' } + It "Tab completion for validateSet attribute takes precedence over enums" { + function foo { param([ValidateSet('DarkBlue','DarkCyan')][ConsoleColor]$p) } + $inputStr = "foo " + $res = TabExpansion2 -inputScript $inputStr -cursorColumn $inputStr.Length + $res.CompletionMatches | Should -HaveCount 2 + $res.CompletionMatches[0].CompletionText | Should -BeExactly 'DarkBlue' + $res.CompletionMatches[1].CompletionText | Should -BeExactly 'DarkCyan' + } + It "Tab completion for ArgumentCompleter when AST is passed to CompleteInput" { $scriptBl = { function Test-Completion {