diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/NewPropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/NewPropertyCommand.cs index d95540d21d..ba4bf137ef 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/NewPropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/NewPropertyCommand.cs @@ -226,31 +226,13 @@ namespace Microsoft.PowerShell.Commands string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) - { - if (!IsRegistryProvider(fakeBoundParameters)) - { - yield break; - } - - string quote = CompletionCompleters.HandleDoubleAndSingleQuote(ref wordToComplete); - var propertyTypePattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); - - foreach (string propertyType in s_RegistryPropertyTypes) - { - if (propertyTypePattern.IsMatch(propertyType)) - { - string completionText = quote == string.Empty - ? propertyType - : quote + propertyType + quote; - - yield return new CompletionResult( - completionText, - propertyType, - CompletionResultType.ParameterValue, - GetRegistryPropertyTypeToolTip(propertyType)); - } - } - } + => IsRegistryProvider(fakeBoundParameters) + ? CompletionCompleters.GetMatchingResults( + wordToComplete, + possibleCompletionValues: s_RegistryPropertyTypes, + toolTipMapping: GetRegistryPropertyTypeToolTip, + resultType: CompletionResultType.ParameterValue) + : []; /// /// Checks if parameter paths are from Registry provider. diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs index 0d6cedfd39..e149ece4c8 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs @@ -2664,7 +2664,7 @@ namespace Microsoft.PowerShell.Commands // -Verb is not supported on non-Windows platforms as well as Windows headless SKUs if (!Platform.IsWindowsDesktop) { - yield break; + return []; } // Completion: Start-Process -FilePath -Verb @@ -2676,12 +2676,7 @@ namespace Microsoft.PowerShell.Commands // Complete file verbs if extension exists if (Path.HasExtension(filePath)) { - foreach (string verb in CompleteFileVerbs(filePath, wordToComplete)) - { - yield return new CompletionResult(verb); - } - - yield break; + return CompleteFileVerbs(wordToComplete, filePath); } // Otherwise check if command is an Application to resolve executable full path with extension @@ -2699,34 +2694,23 @@ namespace Microsoft.PowerShell.Commands // Start-Process & Get-Command select first found application based on PATHEXT environment variable if (commands.Count >= 1) { - foreach (string verb in CompleteFileVerbs(commands[0].Source, wordToComplete)) - { - yield return new CompletionResult(verb); - } + return CompleteFileVerbs(wordToComplete, filePath: commands[0].Source); } } + + return []; } /// /// Completes file verbs. /// - /// The file path to get verbs. /// The word to complete. + /// The file path to get verbs. /// List of file verbs to complete. - private static IEnumerable CompleteFileVerbs(string filePath, string wordToComplete) - { - var verbPattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); - - string[] verbs = new ProcessStartInfo(filePath).Verbs; - - foreach (string verb in verbs) - { - if (verbPattern.IsMatch(verb)) - { - yield return verb; - } - } - } + private static IEnumerable CompleteFileVerbs(string wordToComplete, string filePath) + => CompletionCompleters.GetMatchingResults( + wordToComplete, + possibleCompletionValues: new ProcessStartInfo(filePath).Verbs); } #if !UNIX diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 6e756d2cf9..fe3802f859 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -8382,6 +8382,45 @@ namespace System.Management.Automation return quote; } + /// + /// Get matching completions from word to complete. + /// This makes it easier to handle different variations of completions with consideration of quotes. + /// + /// The word to complete. + /// The possible completion values to iterate. + /// The optional tool tip mapping delegate. + /// The optional completion result type. Default is Text. + /// + internal static IEnumerable GetMatchingResults( + string wordToComplete, + IEnumerable possibleCompletionValues, + Func toolTipMapping = null, + CompletionResultType resultType = CompletionResultType.Text) + { + string quote = HandleDoubleAndSingleQuote(ref wordToComplete); + var pattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); + + foreach (string value in possibleCompletionValues) + { + if (pattern.IsMatch(value)) + { + string completionText = quote == string.Empty + ? value + : quote + value + quote; + + string listItemText = value; + + yield return new CompletionResult( + completionText, + listItemText, + resultType, + toolTip: toolTipMapping is null + ? listItemText + : toolTipMapping(value)); + } + } + } + internal static bool IsSplattedVariable(Ast targetExpr) { if (targetExpr is VariableExpressionAst && ((VariableExpressionAst)targetExpr).Splatted) diff --git a/src/System.Management.Automation/engine/CommandCompletion/ScopeArgumentCompleter.cs b/src/System.Management.Automation/engine/CommandCompletion/ScopeArgumentCompleter.cs index b397be3aa5..8e233bc31c 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/ScopeArgumentCompleter.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/ScopeArgumentCompleter.cs @@ -29,16 +29,8 @@ namespace System.Management.Automation string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) - { - var scopePattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); - - foreach (string scope in s_Scopes) - { - if (scopePattern.IsMatch(scope)) - { - yield return new CompletionResult(scope); - } - } - } + => CompletionCompleters.GetMatchingResults( + wordToComplete, + possibleCompletionValues: s_Scopes); } } diff --git a/src/System.Management.Automation/engine/InternalCommands.cs b/src/System.Management.Automation/engine/InternalCommands.cs index 86502861bf..49bcabddaf 100644 --- a/src/System.Management.Automation/engine/InternalCommands.cs +++ b/src/System.Management.Automation/engine/InternalCommands.cs @@ -2741,17 +2741,9 @@ namespace Microsoft.PowerShell.Commands string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) - { - var strictModeVersionPattern = WildcardPattern.Get(wordToComplete + "*", WildcardOptions.IgnoreCase); - - foreach (string version in s_strictModeVersions) - { - if (strictModeVersionPattern.IsMatch(version)) - { - yield return new CompletionResult(version); - } - } - } + => CompletionCompleters.GetMatchingResults( + wordToComplete, + possibleCompletionValues: s_strictModeVersions); } #endregion Set-StrictMode diff --git a/src/System.Management.Automation/utils/Verbs.cs b/src/System.Management.Automation/utils/Verbs.cs index f06dd2d19e..14a43dca6d 100644 --- a/src/System.Management.Automation/utils/Verbs.cs +++ b/src/System.Management.Automation/utils/Verbs.cs @@ -1332,35 +1332,6 @@ namespace System.Management.Automation /// Verb group display name. private static string GetVerbGroupDisplayName(Type verbType) => verbType.Name.Substring(5); - /// - /// Filters by verbs and commands. - /// - /// The array of verbs. - /// The collection of commands. - /// List of Verbs. - private static IEnumerable FilterByVerbsAndCommands(string[] verbs, Collection commands) - { - if (commands is null || commands.Count == 0) - { - yield break; - } - - Collection verbPatterns = SessionStateUtilities.CreateWildcardsFromStrings( - verbs, - WildcardOptions.IgnoreCase); - - foreach (CmdletInfo command in commands) - { - if (SessionStateUtilities.MatchesAnyWildcardPattern( - command.Verb, - verbPatterns, - defaultValue: false)) - { - yield return command.Verb; - } - } - } - /// /// Filters by verbs and groups. /// @@ -1384,10 +1355,7 @@ namespace System.Management.Automation foreach (Type verbType in VerbTypes) { - if (SessionStateUtilities.CollectionContainsValue( - groups, - GetVerbGroupDisplayName(verbType), - StringComparer.OrdinalIgnoreCase)) + if (GroupsContainVerbType(groups, verbType)) { foreach (VerbInfo verb in FilterVerbsByType(verbs, verbType)) { @@ -1397,6 +1365,62 @@ namespace System.Management.Automation } } + /// + /// Checks if verb type exists in list of groups. + /// + /// The list of groups + /// The verb type to check. + /// True if verb type was found, False if not found. + private static bool GroupsContainVerbType(string[] groups, Type verbType) + => SessionStateUtilities.CollectionContainsValue( + groups, + GetVerbGroupDisplayName(verbType), + StringComparer.OrdinalIgnoreCase); + + /// + /// Enumerates field names from a Verb Type. + /// + /// The verb type. + /// List of field names. + private static IEnumerable EnumerateFieldNamesFromVerbType(Type verbType) + { + foreach (FieldInfo field in verbType.GetFields()) + { + if (field.IsLiteral) + { + yield return field.Name; + } + } + } + + /// + /// Enumerates field names from all Verb Types. + /// + /// List of field names. + private static IEnumerable EnumerateFieldNamesFromAllVerbTypes() + { + foreach (Type verbType in VerbTypes) + { + foreach (string fieldName in EnumerateFieldNamesFromVerbType(verbType)) + { + yield return fieldName; + } + } + } + + /// + /// Enumerates command verb names. + /// + /// The collection of commands. + /// List of command verb names. + private static IEnumerable EnumerateCommandVerbNames(Collection commands) + { + foreach (CmdletInfo command in commands) + { + yield return command.Verb; + } + } + /// /// Filters verbs by type. /// @@ -1407,12 +1431,9 @@ namespace System.Management.Automation { if (verbs is null || verbs.Length == 0) { - foreach (FieldInfo field in verbType.GetFields()) + foreach (string fieldName in EnumerateFieldNamesFromVerbType(verbType)) { - if (field.IsLiteral) - { - yield return CreateVerbFromField(field, verbType); - } + yield return CreateVerbFromField(fieldName, verbType); } yield break; @@ -1422,17 +1443,14 @@ namespace System.Management.Automation verbs, WildcardOptions.IgnoreCase); - foreach (FieldInfo field in verbType.GetFields()) + foreach (string fieldName in EnumerateFieldNamesFromVerbType(verbType)) { - if (field.IsLiteral) - { - if (SessionStateUtilities.MatchesAnyWildcardPattern( - field.Name, + if (SessionStateUtilities.MatchesAnyWildcardPattern( + fieldName, verbPatterns, defaultValue: false)) - { - yield return CreateVerbFromField(field, verbType); - } + { + yield return CreateVerbFromField(fieldName, verbType); } } } @@ -1440,15 +1458,15 @@ namespace System.Management.Automation /// /// Creates Verb info object from field info. /// - /// The field. + /// The field name. /// The verb type. /// VerbInfo object. - private static VerbInfo CreateVerbFromField(FieldInfo field, Type verbType) => new() + private static VerbInfo CreateVerbFromField(string fieldName, Type verbType) => new() { - Verb = field.Name, - AliasPrefix = VerbAliasPrefixes.GetVerbAliasPrefix(field.Name), + Verb = fieldName, + AliasPrefix = VerbAliasPrefixes.GetVerbAliasPrefix(fieldName), Group = GetVerbGroupDisplayName(verbType), - Description = VerbDescriptions.GetVerbDescription(field.Name) + Description = VerbDescriptions.GetVerbDescription(fieldName) }; /// @@ -1472,8 +1490,6 @@ namespace System.Management.Automation CommandAst commandAst, IDictionary fakeBoundParameters) { - var verbs = new string[] { wordToComplete + "*" }; - // Completion: Get-Verb -Group -Verb if (commandName.Equals("Get-Verb", StringComparison.OrdinalIgnoreCase) && fakeBoundParameters.Contains("Group")) @@ -1494,12 +1510,7 @@ namespace System.Management.Automation groups = Array.ConvertAll((object[])groupParameterValue, group => group.ToString()); } - foreach (VerbInfo verb in FilterByVerbsAndGroups(verbs, groups)) - { - yield return new CompletionResult(verb.Verb); - } - - yield break; + return CompleteVerbWithGroups(wordToComplete, groups); } // Completion: Get-Command -Noun -Verb @@ -1520,23 +1531,55 @@ namespace System.Management.Automation Collection commands = ps.Invoke(); - foreach (string verb in FilterByVerbsAndCommands(verbs, commands)) - { - yield return new CompletionResult(verb); - } - - yield break; + return CompleteVerbWithCommands(wordToComplete, commands); } // Complete all verbs by default if above cases not completed + return CompleteVerbForAllTypes(wordToComplete); + } + + /// + /// Completes verb with list of groups. + /// + /// The word to complete. + /// The list of groups. + /// List of completions for verb. + private static IEnumerable CompleteVerbWithGroups(string wordToComplete, string[] groups) + { foreach (Type verbType in VerbTypes) { - foreach (VerbInfo verb in FilterVerbsByType(verbs, verbType)) + if (GroupsContainVerbType(groups, verbType)) { - yield return new CompletionResult(verb.Verb); + foreach (CompletionResult result in CompletionCompleters.GetMatchingResults( + wordToComplete, + possibleCompletionValues: EnumerateFieldNamesFromVerbType(verbType))) + { + yield return result; + } } } } + + /// + /// Completes verb with list of commands. + /// + /// The word to complete. + /// The list of commands. + /// List of completions for verb. + private static IEnumerable CompleteVerbWithCommands(string wordToComplete, Collection commands) + => CompletionCompleters.GetMatchingResults( + wordToComplete, + possibleCompletionValues: EnumerateCommandVerbNames(commands)); + + /// + /// Completes verb for all types. + /// + /// The word to complete. + /// List of completions for verb. + private static IEnumerable CompleteVerbForAllTypes(string wordToComplete) + => CompletionCompleters.GetMatchingResults( + wordToComplete, + possibleCompletionValues: EnumerateFieldNamesFromAllVerbTypes()); } private static readonly Dictionary s_validVerbs = new Dictionary(StringComparer.OrdinalIgnoreCase); diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index e41a2a2fe3..072a2381be 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -771,31 +771,50 @@ ConstructorTestClass(int i, bool b) Context 'Start-Process -Verb parameter completion' { BeforeAll { - function GetProcessInfoVerbs($path) { - (New-Object -TypeName System.Diagnostics.ProcessStartInfo -ArgumentList $path).Verbs + function GetProcessInfoVerbs([string]$path, [switch]$singleQuote, [switch]$doubleQuote) { + $verbs = (New-Object -TypeName System.Diagnostics.ProcessStartInfo -ArgumentList $path).Verbs + + if ($singleQuote) { + return ($verbs | ForEach-Object { "'$_'" }) + } + elseif ($doubleQuote) { + return ($verbs | ForEach-Object { """$_""" }) + } + + return $verbs } $cmdPath = Join-Path -Path $TestDrive -ChildPath 'test.cmd' - $cmdVerbs = GetProcessInfoVerbs $cmdPath + $cmdVerbs = GetProcessInfoVerbs -Path $cmdPath + $cmdVerbsSingleQuote = GetProcessInfoVerbs -Path $cmdPath -SingleQuote + $cmdVerbsDoubleQuote = GetProcessInfoVerbs -Path $cmdPath -DoubleQuote $exePath = Join-Path -Path $TestDrive -ChildPath 'test.exe' - $exeVerbs = GetProcessInfoVerbs $exePath + $exeVerbs = GetProcessInfoVerbs -Path $exePath $exeVerbsStartingWithRun = $exeVerbs | Where-Object { $_ -like 'run*' } + $exeVerbsSingleQuote = GetProcessInfoVerbs -Path $exePath -SingleQuote + $exeVerbsStartingWithRunSingleQuote = $exeVerbsSingleQuote | Where-Object { $_ -like "'run*" } + $exeVerbsDoubleQuote = GetProcessInfoVerbs -Path $exePath -DoubleQuote + $exeVerbsStartingWithRunDoubleQuote = $exeVerbsDoubleQuote | Where-Object { $_ -like """run*" } $powerShellExeWithNoExtension = 'powershell' $txtPath = Join-Path -Path $TestDrive -ChildPath 'test.txt' - $txtVerbs = GetProcessInfoVerbs $txtPath + $txtVerbs = GetProcessInfoVerbs -Path $txtPath $wavPath = Join-Path -Path $TestDrive -ChildPath 'test.wav' - $wavVerbs = GetProcessInfoVerbs $wavPath + $wavVerbs = GetProcessInfoVerbs -Path $wavPath $docxPath = Join-Path -Path $TestDrive -ChildPath 'test.docx' - $docxVerbs = GetProcessInfoVerbs $docxPath + $docxVerbs = GetProcessInfoVerbs -Path $docxPath $fileWithNoExtensionPath = Join-Path -Path $TestDrive -ChildPath 'test' - $fileWithNoExtensionVerbs = GetProcessInfoVerbs $fileWithNoExtensionPath + $fileWithNoExtensionVerbs = GetProcessInfoVerbs -Path $fileWithNoExtensionPath } It "Should complete Verb parameter for ''" -Skip:(!([System.Management.Automation.Platform]::IsWindowsDesktop)) -TestCases @( @{ TextInput = 'Start-Process -Verb '; ExpectedVerbs = '' } @{ TextInput = "Start-Process -FilePath $cmdPath -Verb "; ExpectedVerbs = $cmdVerbs -join ' ' } + @{ TextInput = "Start-Process -FilePath $cmdPath -Verb '"; ExpectedVerbs = $cmdVerbsSingleQuote -join ' ' } + @{ TextInput = "Start-Process -FilePath $cmdPath -Verb """; ExpectedVerbs = $cmdVerbsDoubleQuote -join ' ' } @{ TextInput = "Start-Process -FilePath $exePath -Verb "; ExpectedVerbs = $exeVerbs -join ' ' } @{ TextInput = "Start-Process -FilePath $exePath -Verb run"; ExpectedVerbs = $exeVerbsStartingWithRun -join ' ' } + @{ TextInput = "Start-Process -FilePath $exePath -Verb 'run"; ExpectedVerbs = $exeVerbsStartingWithRunSingleQuote -join ' ' } + @{ TextInput = "Start-Process -FilePath $exePath -Verb ""run"; ExpectedVerbs = $exeVerbsStartingWithRunDoubleQuote -join ' ' } @{ TextInput = "Start-Process -FilePath $powerShellExeWithNoExtension -Verb "; ExpectedVerbs = $exeVerbs -join ' ' } @{ TextInput = "Start-Process -FilePath $txtPath -Verb "; ExpectedVerbs = $txtVerbs -join ' ' } @{ TextInput = "Start-Process -FilePath $wavPath -Verb "; ExpectedVerbs = $wavVerbs -join ' ' } @@ -812,17 +831,33 @@ ConstructorTestClass(int i, bool b) Context 'Scope parameter completion' { BeforeAll { $allScopes = 'Global Local Script' + $allScopesSingleQuote = "'Global' 'Local' 'Script'" + $allScopesDoubleQuote = """Global"" ""Local"" ""Script""" $globalScope = 'Global' + $globalScopeSingleQuote = "'Global'" + $globalScopeDoubleQuote = """Global""" $localScope = 'Local' + $localScopeSingleQuote = "'Local'" + $localScopeDoubleQuote = """Local""" $scriptScope = 'Script' + $scriptScopeSingleQuote = "'Script'" + $scriptScopeDoubleQuote = """Script""" $allScopeCommands = 'Clear-Variable', 'Export-Alias', 'Get-Alias', 'Get-PSDrive', 'Get-Variable', 'Import-Alias', 'New-Alias', 'New-PSDrive', 'New-Variable', 'Remove-Alias', 'Remove-PSDrive', 'Remove-Variable', 'Set-Alias', 'Set-Variable' } It "Should complete '' for ''" -TestCases @( @{ Commands = $allScopeCommands; ParameterInput = "-Scope "; ExpectedScopes = $allScopes } + @{ Commands = $allScopeCommands; ParameterInput = "-Scope '"; ExpectedScopes = $allScopesSingleQuote } + @{ Commands = $allScopeCommands; ParameterInput = "-Scope """; ExpectedScopes = $allScopesDoubleQuote } @{ Commands = $allScopeCommands; ParameterInput = "-Scope G"; ExpectedScopes = $globalScope } + @{ Commands = $allScopeCommands; ParameterInput = "-Scope 'G"; ExpectedScopes = $globalScopeSingleQuote } + @{ Commands = $allScopeCommands; ParameterInput = "-Scope ""G"; ExpectedScopes = $globalScopeDoubleQuote } @{ Commands = $allScopeCommands; ParameterInput = "-Scope Lo"; ExpectedScopes = $localScope } + @{ Commands = $allScopeCommands; ParameterInput = "-Scope 'Lo"; ExpectedScopes = $localScopeSingleQuote } + @{ Commands = $allScopeCommands; ParameterInput = "-Scope ""Lo"; ExpectedScopes = $localScopeDoubleQuote } @{ Commands = $allScopeCommands; ParameterInput = "-Scope Scr"; ExpectedScopes = $scriptScope } + @{ Commands = $allScopeCommands; ParameterInput = "-Scope 'Scr"; ExpectedScopes = $scriptScopeSingleQuote } + @{ Commands = $allScopeCommands; ParameterInput = "-Scope ""Scr"; ExpectedScopes = $scriptScopeDoubleQuote } @{ Commands = $allScopeCommands; ParameterInput = "-Scope NonExistentScope"; ExpectedScopes = '' } ) { param($Commands, $ParameterInput, $ExpectedScopes) @@ -838,28 +873,42 @@ ConstructorTestClass(int i, bool b) Context 'Get-Verb & Get-Command -Verb parameter completion' { BeforeAll { $allVerbs = 'Add Approve Assert Backup Block Build Checkpoint Clear Close Compare Complete Compress Confirm Connect Convert ConvertFrom ConvertTo Copy Debug Deny Deploy Disable Disconnect Dismount Edit Enable Enter Exit Expand Export Find Format Get Grant Group Hide Import Initialize Install Invoke Join Limit Lock Measure Merge Mount Move New Open Optimize Out Ping Pop Protect Publish Push Read Receive Redo Register Remove Rename Repair Request Reset Resize Resolve Restart Restore Resume Revoke Save Search Select Send Set Show Skip Split Start Step Stop Submit Suspend Switch Sync Test Trace Unblock Undo Uninstall Unlock Unprotect Unpublish Unregister Update Use Wait Watch Write' + $allVerbsSingleQuote = "'Add' 'Approve' 'Assert' 'Backup' 'Block' 'Build' 'Checkpoint' 'Clear' 'Close' 'Compare' 'Complete' 'Compress' 'Confirm' 'Connect' 'Convert' 'ConvertFrom' 'ConvertTo' 'Copy' 'Debug' 'Deny' 'Deploy' 'Disable' 'Disconnect' 'Dismount' 'Edit' 'Enable' 'Enter' 'Exit' 'Expand' 'Export' 'Find' 'Format' 'Get' 'Grant' 'Group' 'Hide' 'Import' 'Initialize' 'Install' 'Invoke' 'Join' 'Limit' 'Lock' 'Measure' 'Merge' 'Mount' 'Move' 'New' 'Open' 'Optimize' 'Out' 'Ping' 'Pop' 'Protect' 'Publish' 'Push' 'Read' 'Receive' 'Redo' 'Register' 'Remove' 'Rename' 'Repair' 'Request' 'Reset' 'Resize' 'Resolve' 'Restart' 'Restore' 'Resume' 'Revoke' 'Save' 'Search' 'Select' 'Send' 'Set' 'Show' 'Skip' 'Split' 'Start' 'Step' 'Stop' 'Submit' 'Suspend' 'Switch' 'Sync' 'Test' 'Trace' 'Unblock' 'Undo' 'Uninstall' 'Unlock' 'Unprotect' 'Unpublish' 'Unregister' 'Update' 'Use' 'Wait' 'Watch' 'Write'" + $allVerbsDoubleQuote = """Add"" ""Approve"" ""Assert"" ""Backup"" ""Block"" ""Build"" ""Checkpoint"" ""Clear"" ""Close"" ""Compare"" ""Complete"" ""Compress"" ""Confirm"" ""Connect"" ""Convert"" ""ConvertFrom"" ""ConvertTo"" ""Copy"" ""Debug"" ""Deny"" ""Deploy"" ""Disable"" ""Disconnect"" ""Dismount"" ""Edit"" ""Enable"" ""Enter"" ""Exit"" ""Expand"" ""Export"" ""Find"" ""Format"" ""Get"" ""Grant"" ""Group"" ""Hide"" ""Import"" ""Initialize"" ""Install"" ""Invoke"" ""Join"" ""Limit"" ""Lock"" ""Measure"" ""Merge"" ""Mount"" ""Move"" ""New"" ""Open"" ""Optimize"" ""Out"" ""Ping"" ""Pop"" ""Protect"" ""Publish"" ""Push"" ""Read"" ""Receive"" ""Redo"" ""Register"" ""Remove"" ""Rename"" ""Repair"" ""Request"" ""Reset"" ""Resize"" ""Resolve"" ""Restart"" ""Restore"" ""Resume"" ""Revoke"" ""Save"" ""Search"" ""Select"" ""Send"" ""Set"" ""Show"" ""Skip"" ""Split"" ""Start"" ""Step"" ""Stop"" ""Submit"" ""Suspend"" ""Switch"" ""Sync"" ""Test"" ""Trace"" ""Unblock"" ""Undo"" ""Uninstall"" ""Unlock"" ""Unprotect"" ""Unpublish"" ""Unregister"" ""Update"" ""Use"" ""Wait"" ""Watch"" ""Write""" $verbsStartingWithRe = 'Read Receive Redo Register Remove Rename Repair Request Reset Resize Resolve Restart Restore Resume Revoke' $verbsStartingWithEx = 'Exit Expand Export' $verbsStartingWithConv = 'Convert ConvertFrom ConvertTo' $lifeCycleVerbsStartingWithRe = 'Register Request Restart Resume' + $lifeCycleVerbsStartingWithReSingleQuote = "'Register' 'Request' 'Restart' 'Resume'" + $lifeCycleVerbsStartingWithReDoubleQuote = """Register"" ""Request"" ""Restart"" ""Resume""" $dataVerbsStartingwithEx = 'Expand Export' $lifeCycleAndCommmonVerbsStartingWithRe = 'Redo Register Remove Rename Request Reset Resize Restart Resume' $allLifeCycleAndCommonVerbs = 'Add Approve Assert Build Clear Close Complete Confirm Copy Deny Deploy Disable Enable Enter Exit Find Format Get Hide Install Invoke Join Lock Move New Open Optimize Pop Push Redo Register Remove Rename Request Reset Resize Restart Resume Search Select Set Show Skip Split Start Step Stop Submit Suspend Switch Undo Uninstall Unlock Unregister Wait Watch' $allJsonVerbs = 'ConvertFrom ConvertTo Test' $jsonVerbsStartingWithConv = 'ConvertFrom ConvertTo' + $jsonVerbsStartingWithConvSingleQuote = "'ConvertFrom' 'ConvertTo'" + $jsonVerbsStartingWithConvDoubleQuote = """ConvertFrom"" ""ConvertTo""" $allJsonAndJobVerbs = 'ConvertFrom ConvertTo Debug Get Receive Remove Start Stop Test Wait' $jsonAndJobVerbsStartingWithSt = 'Start Stop' $allObjectVerbs = 'Compare ForEach Group Measure New Select Sort Tee Where' $utilityModuleObjectVerbs = 'Compare Group Measure New Select Sort Tee' $utilityModuleObjectVerbsStartingWithS = 'Select Sort' + $utilityModuleObjectVerbsStartingWithSSingleQuote = "'Select' 'Sort'" + $utilityModuleObjectVerbsStartingWithSDoubleQuote = """Select"" ""Sort""" + $utilityModuleObjectVerbsStartingWithS $coreModuleObjectVerbs = 'ForEach Where' } It "Should complete Verb parameter for ''" -TestCases @( @{ TextInput = 'Get-Verb -Verb '; ExpectedVerbs = $allVerbs } + @{ TextInput = "Get-Verb -Verb '"; ExpectedVerbs = $allVerbsSingleQuote } + @{ TextInput = "Get-Verb -Verb """; ExpectedVerbs = $allVerbsDoubleQuote } @{ TextInput = 'Get-Verb -Group Lifecycle, Common -Verb '; ExpectedVerbs = $allLifeCycleAndCommonVerbs } @{ TextInput = 'Get-Verb -Verb Re'; ExpectedVerbs = $verbsStartingWithRe } @{ TextInput = 'Get-Verb -Group Lifecycle -Verb Re'; ExpectedVerbs = $lifeCycleVerbsStartingWithRe } + @{ TextInput = "Get-Verb -Group Lifecycle -Verb 'Re"; ExpectedVerbs = $lifeCycleVerbsStartingWithReSingleQuote } + @{ TextInput = "Get-Verb -Group Lifecycle -Verb ""Re"; ExpectedVerbs = $lifeCycleVerbsStartingWithReDoubleQuote } + @{ TextInput = 'Get-Verb -Group Lifecycle -Verb Re'; ExpectedVerbs = $lifeCycleVerbsStartingWithRe } @{ TextInput = 'Get-Verb -Group Lifecycle, Common -Verb Re'; ExpectedVerbs = $lifeCycleAndCommmonVerbsStartingWithRe } @{ TextInput = 'Get-Verb -Verb Ex'; ExpectedVerbs = $verbsStartingWithEx } @{ TextInput = 'Get-Verb -Group Data -Verb Ex'; ExpectedVerbs = $dataVerbsStartingwithEx } @@ -871,12 +920,16 @@ ConstructorTestClass(int i, bool b) @{ TextInput = 'Get-Command -Verb Conv'; ExpectedVerbs = $verbsStartingWithConv } @{ TextInput = 'Get-Command -Noun Json -Verb '; ExpectedVerbs = $allJsonVerbs } @{ TextInput = 'Get-Command -Noun Json -Verb Conv'; ExpectedVerbs = $jsonVerbsStartingWithConv } + @{ TextInput = "Get-Command -Noun Json -Verb 'Conv"; ExpectedVerbs = $jsonVerbsStartingWithConvSingleQuote } + @{ TextInput = "Get-Command -Noun Json -Verb ""Conv"; ExpectedVerbs = $jsonVerbsStartingWithConvDoubleQuote } @{ TextInput = 'Get-Command -Noun Json, Job -Verb '; ExpectedVerbs = $allJsonAndJobVerbs } @{ TextInput = 'Get-Command -Noun Json, Job -Verb St'; ExpectedVerbs = $jsonAndJobVerbsStartingWithSt } @{ TextInput = 'Get-Command -Noun NonExistentNoun -Verb '; ExpectedVerbs = '' } @{ TextInput = 'Get-Command -Noun Object -Module Microsoft.PowerShell.Utility,Microsoft.PowerShell.Core -Verb '; ExpectedVerbs = $allObjectVerbs } @{ TextInput = 'Get-Command -Noun Object -Module Microsoft.PowerShell.Utility -Verb '; ExpectedVerbs = $utilityModuleObjectVerbs } @{ TextInput = 'Get-Command -Noun Object -Module Microsoft.PowerShell.Utility -Verb S'; ExpectedVerbs = $utilityModuleObjectVerbsStartingWithS } + @{ TextInput = "Get-Command -Noun Object -Module Microsoft.PowerShell.Utility -Verb 'S"; ExpectedVerbs = $utilityModuleObjectVerbsStartingWithSSingleQuote } + @{ TextInput = "Get-Command -Noun Object -Module Microsoft.PowerShell.Utility -Verb ""S"; ExpectedVerbs = $utilityModuleObjectVerbsStartingWithSDoubleQuote } @{ TextInput = 'Get-Command -Noun Object -Module Microsoft.PowerShell.Core -Verb '; ExpectedVerbs = $coreModuleObjectVerbs } ) { param($TextInput, $ExpectedVerbs) @@ -889,18 +942,26 @@ ConstructorTestClass(int i, bool b) Context 'StrictMode Version parameter completion' { BeforeAll { $allStrictModeVersions = '1.0 2.0 3.0 Latest' + $allStrictModeVersionsSingleQuote = "'1.0' '2.0' '3.0' 'Latest'" + $allStrictModeVersionsDoubleQuote = """1.0"" ""2.0"" ""3.0"" ""Latest""" $versionOne = '1.0' $versionTwo = '2.0' $versionThree = '3.0' $latestVersion = 'Latest' + $latestVersionSingleQuote = "'Latest'" + $latestVersionDoubleQuote = """Latest""" } It "Should complete Version for ''" -TestCases @( @{ TextInput = "Set-StrictMode -Version "; ExpectedVersions = $allStrictModeVersions } + @{ TextInput = "Set-StrictMode -Version '"; ExpectedVersions = $allStrictModeVersionsSingleQuote } + @{ TextInput = "Set-StrictMode -Version """; ExpectedVersions = $allStrictModeVersionsDoubleQuote } @{ TextInput = "Set-StrictMode -Version 1"; ExpectedVersions = $versionOne } @{ TextInput = "Set-StrictMode -Version 2"; ExpectedVersions = $versionTwo } @{ TextInput = "Set-StrictMode -Version 3"; ExpectedVersions = $versionThree } @{ TextInput = "Set-StrictMode -Version Lat"; ExpectedVersions = $latestVersion } + @{ TextInput = "Set-StrictMode -Version 'Lat"; ExpectedVersions = $latestVersionSingleQuote } + @{ TextInput = "Set-StrictMode -Version ""Lat"; ExpectedVersions = $latestVersionDoubleQuote } @{ TextInput = "Set-StrictMode -Version NonExistentVersion"; ExpectedVersions = '' } ) { param($TextInput, $ExpectedVersions)