From 4ec67ca5039f7f65f75fff61d7f4d16323107f2d Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Wed, 10 Aug 2022 01:35:12 +0200 Subject: [PATCH 01/10] Add tooltips for hashtable key completions Add completion for calculated properties for Compare-Object and ConvertTo-HTML Add argument completer for the Property parameter of ConvertTo-Html Use resource files instead of dictionary for comment completion tooltips --- .../CommandCompletion/CompletionCompleters.cs | 120 ++++++---- .../resources/TabCompletionStrings.resx | 212 ++++++++++++++++++ 2 files changed, 289 insertions(+), 43 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 1bf0919cf97..7210f952819 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -2333,6 +2333,16 @@ private static void NativeCommandArgumentCompletion( break; } + case "ConvertTo-Html": + { + if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase)) + { + NativeCompletionMemberName(context, result, commandAst, boundArguments?[parameterName]); + } + + break; + } + case "New-Object": { if (parameterName.Equals("TypeName", StringComparison.OrdinalIgnoreCase)) @@ -5224,12 +5234,16 @@ private static List CompleteRequires(CompletionContext context // Produce completions for all parameters that begin with the prefix we've found, // but which haven't already been specified in the line we need to complete - foreach (KeyValuePair parameter in s_requiresParameters) + foreach (string parameter in s_requiresParameters) { - if (parameter.Key.StartsWith(currentParameterPrefix, StringComparison.OrdinalIgnoreCase) - && !context.CursorPosition.Line.Contains($" -{parameter.Key}", StringComparison.OrdinalIgnoreCase)) + if (parameter.StartsWith(currentParameterPrefix, StringComparison.OrdinalIgnoreCase) + && !context.CursorPosition.Line.Contains($" -{parameter}", StringComparison.OrdinalIgnoreCase)) { - results.Add(new CompletionResult(parameter.Key, parameter.Key, CompletionResultType.ParameterName, parameter.Value)); + string toolTip = ResourceManagerCache.GetResourceString( + typeof(CompletionCompleters).Assembly, + "System.Management.Automation.resources.TabCompletionStrings", + $"Requires{parameter}ParameterDescription"); + results.Add(new CompletionResult(parameter, parameter, CompletionResultType.ParameterName, toolTip)); } } @@ -5254,11 +5268,15 @@ private static List CompleteRequires(CompletionContext context // Complete PSEdition parameter values if (currentParameterMatch.Value.Equals("PSEdition", StringComparison.OrdinalIgnoreCase)) { - foreach (KeyValuePair psEditionEntry in s_requiresPSEditions) + foreach (string psEditionEntry in s_requiresPSEditions) { - if (psEditionEntry.Key.StartsWith(currentValue, StringComparison.OrdinalIgnoreCase)) + if (psEditionEntry.StartsWith(currentValue, StringComparison.OrdinalIgnoreCase)) { - results.Add(new CompletionResult(psEditionEntry.Key, psEditionEntry.Key, CompletionResultType.ParameterValue, psEditionEntry.Value)); + string toolTip = ResourceManagerCache.GetResourceString( + typeof(CompletionCompleters).Assembly, + "System.Management.Automation.resources.TabCompletionStrings", + $"RequiresPsEdition{psEditionEntry}Description"); + results.Add(new CompletionResult(psEditionEntry, psEditionEntry, CompletionResultType.ParameterValue, toolTip)); } } @@ -5286,7 +5304,7 @@ private static List CompleteRequires(CompletionContext context hashtableKeyMatches = Regex.Matches(hashtableString, @"(@{|;)\s*(?:'|\""|\w*)\w*"); // Build the list of keys we might want to complete, based on what's already been provided - var moduleSpecKeysToComplete = new HashSet(s_requiresModuleSpecKeys.Keys); + var moduleSpecKeysToComplete = new HashSet(s_requiresModuleSpecKeys); bool sawModuleNameLast = false; foreach (Match existingHashtableKeyMatch in hashtableKeyMatches) { @@ -5342,7 +5360,11 @@ private static List CompleteRequires(CompletionContext context { if (moduleSpecKey.StartsWith(currentValue, StringComparison.OrdinalIgnoreCase)) { - results.Add(new CompletionResult(moduleSpecKey, moduleSpecKey, CompletionResultType.ParameterValue, s_requiresModuleSpecKeys[moduleSpecKey])); + string toolTip = ResourceManagerCache.GetResourceString( + typeof(CompletionCompleters).Assembly, + "System.Management.Automation.resources.TabCompletionStrings", + $"RequiresModuleSpec{moduleSpecKey}Description"); + results.Add(new CompletionResult(moduleSpecKey, moduleSpecKey, CompletionResultType.ParameterValue, toolTip)); } } } @@ -5350,27 +5372,27 @@ private static List CompleteRequires(CompletionContext context return results; } - private static readonly IReadOnlyDictionary s_requiresParameters = new SortedList(StringComparer.OrdinalIgnoreCase) + private static readonly string[] s_requiresParameters = new string[] { - { "Modules", "Specifies PowerShell modules that the script requires." }, - { "PSEdition", "Specifies a PowerShell edition that the script requires." }, - { "RunAsAdministrator", "Specifies that PowerShell must be running as administrator on Windows." }, - { "Version", "Specifies the minimum version of PowerShell that the script requires." }, + "Modules", + "PSEdition", + "RunAsAdministrator", + "Version" }; - private static readonly IReadOnlyDictionary s_requiresPSEditions = new SortedList(StringComparer.OrdinalIgnoreCase) + private static readonly string[] s_requiresPSEditions = new string[] { - { "Core", "Specifies that the script requires PowerShell Core to run." }, - { "Desktop", "Specifies that the script requires Windows PowerShell to run." }, + "Core", + "Desktop" }; - private static readonly IReadOnlyDictionary s_requiresModuleSpecKeys = new SortedList(StringComparer.OrdinalIgnoreCase) + private static readonly string[] s_requiresModuleSpecKeys = new string[] { - { "ModuleName", "Required. Specifies the module name." }, - { "GUID", "Optional. Specifies the GUID of the module." }, - { "ModuleVersion", "Specifies a minimum acceptable version of the module." }, - { "RequiredVersion", "Specifies an exact, required version of the module." }, - { "MaximumVersion", "Specifies the maximum acceptable version of the module." }, + "ModuleName", + "GUID", + "ModuleVersion", + "RequiredVersion", + "MaximumVersion" }; private static readonly char[] s_hashtableKeyPrefixes = new[] @@ -5415,7 +5437,7 @@ private static List CompleteCommentHelp(CompletionContext cont replacementIndex = context.TokenAtCursor.Extent.StartOffset + lineKeyword.Index; replacementLength = lineKeyword.Value.Length; - var validKeywords = new HashSet(s_commentHelpKeywords.Keys, StringComparer.OrdinalIgnoreCase); + var validKeywords = new HashSet(s_commentHelpKeywords, StringComparer.OrdinalIgnoreCase); foreach (Match keyword in usedKeywords) { if (keyword == lineKeyword || s_commentHelpAllowedDuplicateKeywords.Contains(keyword.Value)) @@ -5431,7 +5453,11 @@ private static List CompleteCommentHelp(CompletionContext cont { if (keyword.StartsWith(lineKeyword.Value, StringComparison.OrdinalIgnoreCase)) { - result.Add(new CompletionResult(keyword, keyword, CompletionResultType.Keyword, s_commentHelpKeywords[keyword])); + string toolTip = ResourceManagerCache.GetResourceString( + typeof(CompletionCompleters).Assembly, + "System.Management.Automation.resources.TabCompletionStrings", + $"CommentHelp{keyword}KeywordDescription"); + result.Add(new CompletionResult(keyword, keyword, CompletionResultType.Keyword, toolTip)); } } @@ -5491,23 +5517,23 @@ private static List CompleteCommentHelp(CompletionContext cont return null; } - private static readonly IReadOnlyDictionary s_commentHelpKeywords = new SortedList(StringComparer.OrdinalIgnoreCase) - { - { "SYNOPSIS", "A brief description of the function or script. This keyword can be used only once in each topic." }, - { "DESCRIPTION", "A detailed description of the function or script. This keyword can be used only once in each topic." }, - { "PARAMETER", ".PARAMETER \nThe description of a parameter. Add a .PARAMETER keyword for each parameter in the function or script syntax." }, - { "EXAMPLE", "A sample command that uses the function or script, optionally followed by sample output and a description. Repeat this keyword for each example." }, - { "INPUTS", "The .NET types of objects that can be piped to the function or script. You can also include a description of the input objects." }, - { "OUTPUTS", "The .NET type of the objects that the cmdlet returns. You can also include a description of the returned objects." }, - { "NOTES", "Additional information about the function or script." }, - { "LINK", "The name of a related topic. Repeat the .LINK keyword for each related topic. The .Link keyword content can also include a URI to an online version of the same help topic." }, - { "COMPONENT", "The name of the technology or feature that the function or script uses, or to which it is related." }, - { "ROLE", "The name of the user role for the help topic." }, - { "FUNCTIONALITY", "The keywords that describe the intended use of the function." }, - { "FORWARDHELPTARGETNAME", ".FORWARDHELPTARGETNAME \nRedirects to the help topic for the specified command." }, - { "FORWARDHELPCATEGORY", ".FORWARDHELPCATEGORY \nSpecifies the help category of the item in .ForwardHelpTargetName" }, - { "REMOTEHELPRUNSPACE", ".REMOTEHELPRUNSPACE \nSpecifies a session that contains the help topic. Enter a variable that contains a PSSession object." }, - { "EXTERNALHELP", ".EXTERNALHELP \nThe .ExternalHelp keyword is required when a function or script is documented in XML files." } + private static readonly string[] s_commentHelpKeywords = new string[] + { + "SYNOPSIS", + "DESCRIPTION", + "PARAMETER", + "EXAMPLE", + "INPUTS", + "OUTPUTS", + "NOTES", + "LINK", + "COMPONENT", + "ROLE", + "FUNCTIONALITY", + "FORWARDHELPTARGETNAME", + "FORWARDHELPCATEGORY", + "REMOTEHELPRUNSPACE", + "EXTERNALHELP" }; private static readonly HashSet s_commentHelpAllowedDuplicateKeywords = new(StringComparer.OrdinalIgnoreCase) @@ -7226,7 +7252,10 @@ internal static List CompleteHashtableKey(CompletionContext co case "Sort-Object": return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Ascending", "Descending"); case "Group-Object": + case "Compare-Object": return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression"); + case "ConvertTo-Html": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Label", "Width", "Alignment"); case "Format-Table": return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label", "Width", "Alignment"); case "Format-List": @@ -7331,7 +7360,12 @@ private static List GetSpecialHashTableKeyMembers(HashSet Shift Right bit operator. Inserts zero in the left-most bit position. For signed values, sign bit is preserved. + + [string] +Specifies the name of the property being created. + + + [string] +Specifies the name of the property being created. + + + [scriptblock] +A script block used to calculate the value of the new property. + + + [string] +Define how the values are displayed in a column. +Valid values are 'left', 'center', or 'right'. + + + [string] +Specifies a format string that defines how the value is formatted for output. + + + [int] +Specifies the maximum column width in a table when the value is displayed. +The value must be greater than 0. + + + [int] +The depth key allows you to specify the depth of expansion per property. + + + [bool] +Allows you to specify the order of sorting for one or more properties. + + + [bool] +Allows you to specify the order of sorting for one or more properties. + + + [String[]] +Specifies the log names to get events from. +Supports wildcards. + + + [String[]] +Specifies the event log providers to get events from. +Supports wildcards. + + + [String[]] +Specifies file paths to log files to get events from. +Valid file formats are: .etl, .evt, and .evtx + + + [Long[]] +Selects events with the specified keyword bitmasks. +The following are standard keywords: +4503599627370496: AuditFailure +9007199254740992: AuditSuccess +4503599627370496: CorrelationHint +18014398509481984: CorrelationHint2 +36028797018963968: EventLogClassic +281474976710656: ResponseTime +2251799813685248: Sqm +562949953421312: WdiContext +1125899906842624: WdiDiagnostic + + + [int[]] +Selects events with the specified event IDs. + + + [int[]] +Selects events with the specified log levels. +You can use the following log levels: +1: Critical +2: Error +3: Warning +4: Informational +5: Verbose + + + [datetime] +Selects events created after the specified date and time. + + + [datetime] +Selects events created before the specified date and time. + + + [string] +Selects events generated by the specified user. +This can either be a string representation of a SID or a domain and username in the format DOMAIN\USERNAME or USERNAME@DOMAIN + + + [string[]] +Selects events with any of the specified values in the EventData section. + + + [hashtable] +Excludes events that match the values specified in the hashtable. + + + [string] or [hashtable] +Specifies an array of PowerShell modules that the script requires. +Each element can either be a string with the module name as value or a hashtable with the following keys: +Name: Name of the module +GUID: GUID of the module +One of the following: +ModuleVersion: Specifies a minimum acceptable version of the module. +RequiredVersion: Specifies an exact, required version of the module. +MaximumVersion: Specifies the maximum acceptable version of the module. + + + [string] +Specifies a PowerShell edition that the script requires. +Valid values are "Core" and "Desktop" + + + [switch] +Specifies that PowerShell must be running as administrator on Windows. +This must be the last parameter on the #requires statement line. + + + [version] +Specifies the minimum version of PowerShell that the script requires. + + + Specifies that the script requires PowerShell Core to run. + + + Specifies that the script requires Windows PowerShell to run. + + + [string] +Required. Specifies the module name. + + + [string] +Optional. Specifies the GUID of the module. + + + [string] +Specifies a minimum acceptable version of the module. + + + [string] +Specifies an exact, required version of the module. + + + [string] +Specifies the maximum acceptable version of the module. + + + A brief description of the function or script. +This keyword can be used only once in each topic. + + + A detailed description of the function or script. +This keyword can be used only once in each topic. + + + .PARAMETER <Parameter-Name> +The description of a parameter. +Add a .PARAMETER keyword for each parameter in the function or script syntax. + + + A sample command that uses the function or script, optionally followed by sample output and a description. +Repeat this keyword for each example. + + + The .NET types of objects that can be piped to the function or script. +You can also include a description of the input objects. + + + The .NET type of the objects that the cmdlet returns. +You can also include a description of the returned objects. + + + Additional information about the function or script. + + + The name of a related topic. +Repeat the .LINK keyword for each related topic. +The .Link keyword content can also include a URI to an online version of the same help topic. + + + The name of the technology or feature that the function or script uses, or to which it is related. + + + The name of the user role for the help topic. + + + The keywords that describe the intended use of the function. + + + .FORWARDHELPTARGETNAME <Command-Name> +Redirects to the help topic for the specified command. + + + .FORWARDHELPCATEGORY <Category> +Specifies the help category of the item in .ForwardHelpTargetName + + + .REMOTEHELPRUNSPACE <PSSession-variable> +Specifies a session that contains the help topic. +Enter a variable that contains a PSSession object. + + + .EXTERNALHELP <XML Help File> +The .ExternalHelp keyword is required when a function or script is documented in XML files. + From da04c6fff427563586634e365f678c07150f4cac Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Wed, 10 Aug 2022 20:01:32 +0200 Subject: [PATCH 02/10] Fix tests --- .../resources/TabCompletionStrings.resx | 2 +- .../TabCompletion/TabCompletion.Tests.ps1 | 34 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/System.Management.Automation/resources/TabCompletionStrings.resx b/src/System.Management.Automation/resources/TabCompletionStrings.resx index e9d1e4bd4bb..4678f525226 100644 --- a/src/System.Management.Automation/resources/TabCompletionStrings.resx +++ b/src/System.Management.Automation/resources/TabCompletionStrings.resx @@ -346,7 +346,7 @@ A script block used to calculate the value of the new property. Define how the values are displayed in a column. Valid values are 'left', 'center', or 'right'. - + [string] Specifies a format string that defines how the value is formatted for output. diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 4ff31245a31..3866d702d57 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -1423,9 +1423,9 @@ ConstructorTestClass(int i, bool b) } It "Test hashtable key completion in #requires statement for modules" { - $res = TabExpansion2 -inputScript "#requires -Modules @{" -cursorColumn 21 + $res = TabExpansion2 -inputScript "#requires -Modules @{" $res.CompletionMatches.Count | Should -BeGreaterThan 0 - $res.CompletionMatches[0].CompletionText | Should -BeExactly "GUID" + $res.CompletionMatches[0].CompletionText | Should -BeExactly "ModuleName" } It "Test no suggestions for already existing hashtable keys in #requires statement for modules" { @@ -2037,21 +2037,21 @@ dir -Recurse ` @{ Intent = 'Complete help keywords with minimum input' Expected = @( - 'COMPONENT' - 'DESCRIPTION' - 'EXAMPLE' - 'EXTERNALHELP' - 'FORWARDHELPCATEGORY' - 'FORWARDHELPTARGETNAME' - 'FUNCTIONALITY' - 'INPUTS' - 'LINK' - 'NOTES' - 'OUTPUTS' - 'PARAMETER' - 'REMOTEHELPRUNSPACE' - 'ROLE' - 'SYNOPSIS' + "SYNOPSIS", + "DESCRIPTION", + "PARAMETER", + "EXAMPLE", + "INPUTS", + "OUTPUTS", + "NOTES", + "LINK", + "COMPONENT", + "ROLE", + "FUNCTIONALITY", + "FORWARDHELPTARGETNAME", + "FORWARDHELPCATEGORY", + "REMOTEHELPRUNSPACE", + "EXTERNALHELP" ) TestString = @' <# From e8104e8d371caded75a974df9b644a0b3de75c19 Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Tue, 2 May 2023 23:15:23 +0200 Subject: [PATCH 03/10] Fix merge issues. --- .../CommandCompletion/CompletionCompleters.cs | 77 ++++++++++--------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index eee17d62d05..5966d0ffeac 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -7384,7 +7384,7 @@ internal static List CompleteHashtableKey(CompletionContext co parentAst = parentAst.Parent; } - ExitWhileLoop: + ExitWhileLoop: bool hashtableIsNested = nestedHashtableKeys.Count > 0; int cursorOffset = completionContext.CursorPosition.Offset; @@ -7502,44 +7502,47 @@ internal static List CompleteHashtableKey(CompletionContext co if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase)) { - switch (binding.CommandName) + if (!hashtableIsNested) { - case "New-Object": - var inferredType = AstTypeInference.InferTypeOf(commandAst, completionContext.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval); - var result = new List(); - CompleteMemberByInferredType( - completionContext.TypeInferenceContext, inferredType, - result, completionContext.WordToComplete + "*", IsWriteablePropertyMember, isStatic: false, excludedKeys); - return result; - case "Select-Object": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Name", "Expression"); - case "Sort-Object": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Ascending", "Descending"); - case "Group-Object": - case "Compare-Object": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression"); - case "ConvertTo-Html": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Label", "Width", "Alignment"); - case "Format-Table": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label", "Width", "Alignment"); - case "Format-List": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label"); - case "Format-Wide": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString"); - case "Format-Custom": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Depth"); - case "Set-CimInstance": - case "New-CimInstance": - var results = new List(); - NativeCompletionCimCommands(parameterName, binding.BoundArguments, results, commandAst, completionContext, excludedKeys, binding.CommandName); - // this method adds a null CompletionResult to the list but we don't want that here. - if (results.Count > 1) - { - results.RemoveAt(results.Count - 1); + switch (binding.CommandName) + { + case "New-Object": + var inferredType = AstTypeInference.InferTypeOf(commandAst, completionContext.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval); + results = new List(); + CompleteMemberByInferredType( + completionContext.TypeInferenceContext, inferredType, + results, completionContext.WordToComplete + "*", IsWriteablePropertyMember, isStatic: false, excludedKeys); return results; - } - - return null; + case "Select-Object": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Name", "Expression"); + case "Sort-Object": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Ascending", "Descending"); + case "Group-Object": + case "Compare-Object": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression"); + case "ConvertTo-Html": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Label", "Width", "Alignment"); + case "Format-Table": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label", "Width", "Alignment"); + case "Format-List": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label"); + case "Format-Wide": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString"); + case "Format-Custom": + return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Depth"); + case "Set-CimInstance": + case "New-CimInstance": + results = new List(); + NativeCompletionCimCommands(parameterName, binding.BoundArguments, results, commandAst, completionContext, excludedKeys, binding.CommandName); + // this method adds a null CompletionResult to the list but we don't want that here. + if (results.Count > 1) + { + results.RemoveAt(results.Count - 1); + return results; + } + return null; + } + return null; } } From 98adf495ae52c5995fcaa236bb8ddf00362aae3f Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Wed, 3 May 2023 01:18:36 +0200 Subject: [PATCH 04/10] Fix merge another merge conflict. --- .../engine/CommandCompletion/CompletionCompleters.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 5966d0ffeac..d75a44cfe4b 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -7407,7 +7407,7 @@ internal static List CompleteHashtableKey(CompletionContext co } var result = new List(); - foreach (var key in s_requiresModuleSpecKeys.Keys) + foreach (var key in s_requiresModuleSpecKeys) { if (excludedKeys.Contains(key) || (wordToComplete is not null && !key.StartsWith(wordToComplete, StringComparison.OrdinalIgnoreCase)) @@ -7416,7 +7416,13 @@ internal static List CompleteHashtableKey(CompletionContext co { continue; } - result.Add(new CompletionResult(key, key, CompletionResultType.Property, s_requiresModuleSpecKeys[key])); + + string toolTip = ResourceManagerCache.GetResourceString( + typeof(CompletionCompleters).Assembly, + "System.Management.Automation.resources.TabCompletionStrings", + $"RequiresModuleSpec{key}Description"); + + result.Add(new CompletionResult(key, key, CompletionResultType.Property, toolTip)); } return result; From 2c20a42d7553d8e92d0d1093383f1c6e651f92ff Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Wed, 3 May 2023 16:39:08 +0200 Subject: [PATCH 05/10] Fix test --- test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index f4598980acc..7b78d037fbc 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -2456,7 +2456,7 @@ function MyFunction ($param1, $param2) It 'Should complete module specification keys in using module statement' { $res = TabExpansion2 -inputScript 'using module @{' - $res.CompletionMatches.CompletionText -join ' ' | Should -BeExactly "GUID MaximumVersion ModuleName ModuleVersion RequiredVersion" + $res.CompletionMatches.CompletionText -join ' ' | Should -BeExactly "ModuleName GUID ModuleVersion RequiredVersion MaximumVersion" } } From 2a04d1afcf37939cc95dda67c40c66d3bd967253 Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Mon, 19 Jun 2023 20:15:56 +0200 Subject: [PATCH 06/10] Sort results and add tooltip test. --- .../CommandCompletion/CompletionCompleters.cs | 44 +++++++++---------- .../TabCompletion/TabCompletion.Tests.ps1 | 27 ++++++------ 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index d75a44cfe4b..cdc9d7d8df1 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5456,11 +5456,11 @@ private static List CompleteRequires(CompletionContext context private static readonly string[] s_requiresModuleSpecKeys = new string[] { - "ModuleName", "GUID", + "MaximumVersion", + "ModuleName", "ModuleVersion", - "RequiredVersion", - "MaximumVersion" + "RequiredVersion" }; private static readonly char[] s_hashtableKeyPrefixes = new[] @@ -5587,44 +5587,44 @@ private static List CompleteCommentHelp(CompletionContext cont private static readonly string[] s_commentHelpKeywords = new string[] { - "SYNOPSIS", + "COMPONENT", "DESCRIPTION", - "PARAMETER", "EXAMPLE", + "EXTERNALHELP", + "FORWARDHELPCATEGORY", + "FORWARDHELPTARGETNAME", + "FUNCTIONALITY", "INPUTS", - "OUTPUTS", - "NOTES", "LINK", - "COMPONENT", - "ROLE", - "FUNCTIONALITY", - "FORWARDHELPTARGETNAME", - "FORWARDHELPCATEGORY", + "NOTES", + "OUTPUTS", + "PARAMETER", "REMOTEHELPRUNSPACE", - "EXTERNALHELP" + "ROLE", + "SYNOPSIS" }; private static readonly HashSet s_commentHelpAllowedDuplicateKeywords = new(StringComparer.OrdinalIgnoreCase) { - "PARAMETER", "EXAMPLE", - "LINK" + "LINK", + "PARAMETER" }; private static readonly string[] s_commentHelpForwardCategories = new string[] { "Alias", + "All", "Cmdlet", - "HelpFile", + "ExternalScript", + "FAQ", + "Filter", "Function", - "Provider", "General", - "FAQ", "Glossary", - "ScriptCommand", - "ExternalScript", - "Filter", - "All" + "HelpFile", + "Provider", + "ScriptCommand" }; private static FunctionDefinitionAst GetCommentHelpFunctionTarget(CompletionContext context) diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index 7b78d037fbc..3ac9f6aa981 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -1596,7 +1596,7 @@ class InheritedClassTest : System.Attribute It "Test hashtable key completion in #requires statement for modules" { $res = TabExpansion2 -inputScript "#requires -Modules @{" $res.CompletionMatches.Count | Should -BeGreaterThan 0 - $res.CompletionMatches[0].CompletionText | Should -BeExactly "ModuleName" + $res.CompletionMatches[0].CompletionText | Should -BeExactly "GUID" } It "Test no suggestions for already existing hashtable keys in #requires statement for modules" { @@ -2251,23 +2251,23 @@ dir -Recurse ` } It '' -TestCases @( @{ - Intent = 'Complete help keywords with minimum input' + Intent = 'Complete help keywords with minimal input' Expected = @( - "SYNOPSIS", + "COMPONENT", "DESCRIPTION", - "PARAMETER", "EXAMPLE", + "EXTERNALHELP", + "FORWARDHELPCATEGORY", + "FORWARDHELPTARGETNAME", + "FUNCTIONALITY", "INPUTS", - "OUTPUTS", - "NOTES", "LINK", - "COMPONENT", - "ROLE", - "FUNCTIONALITY", - "FORWARDHELPTARGETNAME", - "FORWARDHELPCATEGORY", + "NOTES", + "OUTPUTS", + "PARAMETER", "REMOTEHELPRUNSPACE", - "EXTERNALHELP" + "ROLE", + "SYNOPSIS" ) TestString = @' <# @@ -2456,7 +2456,8 @@ function MyFunction ($param1, $param2) It 'Should complete module specification keys in using module statement' { $res = TabExpansion2 -inputScript 'using module @{' - $res.CompletionMatches.CompletionText -join ' ' | Should -BeExactly "ModuleName GUID ModuleVersion RequiredVersion MaximumVersion" + $res.CompletionMatches.CompletionText -join ' ' | Should -BeExactly "GUID MaximumVersion ModuleName ModuleVersion RequiredVersion" + $res.CompletionMatches[0].ToolTip | Should -Not -Be $res.CompletionMatches[0].CompletionText } } From 8a5837380eca5d7b8410647c28b96b3d0f876c73 Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Thu, 20 Mar 2025 21:17:28 +0100 Subject: [PATCH 07/10] Remove irrelevant changes --- .../CommandCompletion/CompletionCompleters.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index c3c93051914..7c2437d10a6 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -2477,16 +2477,6 @@ private static void NativeCommandArgumentCompletion( break; } - case "ConvertTo-Html": - { - if (parameterName.Equals("Property", StringComparison.OrdinalIgnoreCase)) - { - NativeCompletionMemberName(context, result, commandAst, boundArguments?[parameterName]); - } - - break; - } - case "New-Object": { if (parameterName.Equals("TypeName", StringComparison.OrdinalIgnoreCase)) @@ -8194,7 +8184,7 @@ internal static List CompleteHashtableKey(CompletionContext co parentAst = parentAst.Parent; } - ExitWhileLoop: + ExitWhileLoop: bool hashtableIsNested = nestedHashtableKeys.Count > 0; int cursorOffset = completionContext.CursorPosition.Offset; @@ -8334,10 +8324,7 @@ internal static List CompleteHashtableKey(CompletionContext co case "Sort-Object": return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Ascending", "Descending"); case "Group-Object": - case "Compare-Object": return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression"); - case "ConvertTo-Html": - return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "Label", "Width", "Alignment"); case "Format-Table": return GetSpecialHashTableKeyMembers(excludedKeys, wordToComplete, "Expression", "FormatString", "Label", "Width", "Alignment"); case "Format-List": From 8d18d034663cc83b4e1cded8aaff35b67ad43ca0 Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Sun, 23 Mar 2025 16:09:07 +0100 Subject: [PATCH 08/10] Use suggesed pattern to get tooltips --- .../CommandCompletion/CompletionCompleters.cs | 96 ++++++++++++++----- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 7c2437d10a6..44b7a369e22 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -6119,10 +6119,7 @@ private static List CompleteRequires(CompletionContext context if (parameter.StartsWith(currentParameterPrefix, StringComparison.OrdinalIgnoreCase) && !context.CursorPosition.Line.Contains($" -{parameter}", StringComparison.OrdinalIgnoreCase)) { - string toolTip = ResourceManagerCache.GetResourceString( - typeof(CompletionCompleters).Assembly, - "System.Management.Automation.resources.TabCompletionStrings", - $"Requires{parameter}ParameterDescription"); + string toolTip = GetRequiresParametersToolTip(parameter); results.Add(new CompletionResult(parameter, parameter, CompletionResultType.ParameterName, toolTip)); } } @@ -6152,10 +6149,7 @@ private static List CompleteRequires(CompletionContext context { if (psEditionEntry.StartsWith(currentValue, StringComparison.OrdinalIgnoreCase)) { - string toolTip = ResourceManagerCache.GetResourceString( - typeof(CompletionCompleters).Assembly, - "System.Management.Automation.resources.TabCompletionStrings", - $"RequiresPsEdition{psEditionEntry}Description"); + string toolTip = GetRequiresPsEditionsToolTip(psEditionEntry); results.Add(new CompletionResult(psEditionEntry, psEditionEntry, CompletionResultType.ParameterValue, toolTip)); } } @@ -6240,10 +6234,7 @@ private static List CompleteRequires(CompletionContext context { if (moduleSpecKey.StartsWith(currentValue, StringComparison.OrdinalIgnoreCase)) { - string toolTip = ResourceManagerCache.GetResourceString( - typeof(CompletionCompleters).Assembly, - "System.Management.Automation.resources.TabCompletionStrings", - $"RequiresModuleSpec{moduleSpecKey}Description"); + string toolTip = GetRequiresModuleSpecKeysToolTip(moduleSpecKey); results.Add(new CompletionResult(moduleSpecKey, moduleSpecKey, CompletionResultType.ParameterValue, toolTip)); } } @@ -6260,12 +6251,28 @@ private static List CompleteRequires(CompletionContext context "Version" }; + private static string GetRequiresParametersToolTip(string name) => name switch + { + "Modules" => TabCompletionStrings.RequiresModulesParameterDescription, + "PSEdition" => TabCompletionStrings.RequiresPSEditionParameterDescription, + "RunAsAdministrator" => TabCompletionStrings.RequiresRunAsAdministratorParameterDescription, + "Version" => TabCompletionStrings.RequiresVersionParameterDescription, + _ => string.Empty + }; + private static readonly string[] s_requiresPSEditions = new string[] { "Core", "Desktop" }; + private static string GetRequiresPsEditionsToolTip(string name) => name switch + { + "Core" => TabCompletionStrings.RequiresPsEditionCoreDescription, + "Desktop" => TabCompletionStrings.RequiresPsEditionDesktopDescription, + _ => string.Empty + }; + private static readonly string[] s_requiresModuleSpecKeys = new string[] { "GUID", @@ -6275,6 +6282,16 @@ private static List CompleteRequires(CompletionContext context "RequiredVersion" }; + private static string GetRequiresModuleSpecKeysToolTip(string name) => name switch + { + "GUID" => TabCompletionStrings.RequiresModuleSpecGUIDDescription, + "MaximumVersion" => TabCompletionStrings.RequiresModuleSpecMaximumVersionDescription, + "ModuleName" => TabCompletionStrings.RequiresModuleSpecModuleNameDescription, + "ModuleVersion" => TabCompletionStrings.RequiresModuleSpecModuleVersionDescription, + "RequiredVersion" => TabCompletionStrings.RequiresModuleSpecRequiredVersionDescription, + _ => string.Empty + }; + private static readonly char[] s_hashtableKeyPrefixes = new[] { '@', @@ -6333,10 +6350,7 @@ private static List CompleteCommentHelp(CompletionContext cont { if (keyword.StartsWith(lineKeyword.Value, StringComparison.OrdinalIgnoreCase)) { - string toolTip = ResourceManagerCache.GetResourceString( - typeof(CompletionCompleters).Assembly, - "System.Management.Automation.resources.TabCompletionStrings", - $"CommentHelp{keyword}KeywordDescription"); + string toolTip = GetCommentHelpKeywordsToolTip(keyword); result.Add(new CompletionResult(keyword, keyword, CompletionResultType.Keyword, toolTip)); } } @@ -6416,6 +6430,26 @@ private static List CompleteCommentHelp(CompletionContext cont "SYNOPSIS" }; + private static string GetCommentHelpKeywordsToolTip(string name) => name switch + { + "COMPONENT" => TabCompletionStrings.CommentHelpCOMPONENTKeywordDescription, + "DESCRIPTION" => TabCompletionStrings.CommentHelpDESCRIPTIONKeywordDescription, + "EXAMPLE" => TabCompletionStrings.CommentHelpEXAMPLEKeywordDescription, + "EXTERNALHELP" => TabCompletionStrings.CommentHelpEXTERNALHELPKeywordDescription, + "FORWARDHELPCATEGORY" => TabCompletionStrings.CommentHelpFORWARDHELPCATEGORYKeywordDescription, + "FORWARDHELPTARGETNAME" => TabCompletionStrings.CommentHelpFORWARDHELPTARGETNAMEKeywordDescription, + "FUNCTIONALITY" => TabCompletionStrings.CommentHelpFUNCTIONALITYKeywordDescription, + "INPUTS" => TabCompletionStrings.CommentHelpINPUTSKeywordDescription, + "LINK" => TabCompletionStrings.CommentHelpLINKKeywordDescription, + "NOTES" => TabCompletionStrings.CommentHelpNOTESKeywordDescription, + "OUTPUTS" => TabCompletionStrings.CommentHelpOUTPUTSKeywordDescription, + "PARAMETER" => TabCompletionStrings.CommentHelpPARAMETERKeywordDescription, + "REMOTEHELPRUNSPACE" => TabCompletionStrings.CommentHelpREMOTEHELPRUNSPACEKeywordDescription, + "ROLE" => TabCompletionStrings.CommentHelpROLEKeywordDescription, + "SYNOPSIS" => TabCompletionStrings.CommentHelpSYNOPSISKeywordDescription, + _ => string.Empty + }; + private static readonly HashSet s_commentHelpAllowedDuplicateKeywords = new(StringComparer.OrdinalIgnoreCase) { "EXAMPLE", @@ -8217,10 +8251,7 @@ internal static List CompleteHashtableKey(CompletionContext co continue; } - string toolTip = ResourceManagerCache.GetResourceString( - typeof(CompletionCompleters).Assembly, - "System.Management.Automation.resources.TabCompletionStrings", - $"RequiresModuleSpec{key}Description"); + string toolTip = GetRequiresModuleSpecKeysToolTip(key); result.Add(new CompletionResult(key, key, CompletionResultType.Property, toolTip)); } @@ -8474,10 +8505,7 @@ private static List GetSpecialHashTableKeyMembers(HashSet GetSpecialHashTableKeyMembers(HashSet name switch + { + "Alignment" => TabCompletionStrings.AlignmentHashtableKeyDescription, + "Ascending" => TabCompletionStrings.AscendingHashtableKeyDescription, + "Depth" => TabCompletionStrings.DepthHashtableKeyDescription, + "Descending" => TabCompletionStrings.DescendingHashtableKeyDescription, + "Expression" => TabCompletionStrings.ExpressionHashtableKeyDescription, + "FormatString" => TabCompletionStrings.FormatStringHashtableKeyDescription, + "ID" => TabCompletionStrings.IDHashtableKeyDescription, + "Keywords" => TabCompletionStrings.KeywordsHashtableKeyDescription, + "Label" => TabCompletionStrings.LabelHashtableKeyDescription, + "Level" => TabCompletionStrings.LevelHashtableKeyDescription, + "LogName" => TabCompletionStrings.LogNameHashtableKeyDescription, + "Name" => TabCompletionStrings.NameHashtableKeyDescription, + "Path" => TabCompletionStrings.PathHashtableKeyDescription, + "ProviderName" => TabCompletionStrings.ProviderNameHashtableKeyDescription, + "Width" => TabCompletionStrings.WidthHashtableKeyDescription, + _ => string.Empty + }; + #endregion Hashtable Keys #region Helpers From c6df51712b7581920114b89dad6e5778bb3178be Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Mon, 24 Mar 2025 11:27:28 +0100 Subject: [PATCH 09/10] Address feedback --- .../engine/CommandCompletion/CompletionCompleters.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 44b7a369e22..b73bcd5a036 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -8241,7 +8241,7 @@ internal static List CompleteHashtableKey(CompletionContext co } var result = new List(); - foreach (var key in s_requiresModuleSpecKeys) + foreach (string key in s_requiresModuleSpecKeys) { if (excludedKeys.Contains(key) || (wordToComplete is not null && !key.StartsWith(wordToComplete, StringComparison.OrdinalIgnoreCase)) @@ -8523,8 +8523,10 @@ private static List GetSpecialHashTableKeyMembers(HashSet TabCompletionStrings.AlignmentHashtableKeyDescription, "Ascending" => TabCompletionStrings.AscendingHashtableKeyDescription, + "Data" => TabCompletionStrings.DataHashtableKeyDescription, "Depth" => TabCompletionStrings.DepthHashtableKeyDescription, "Descending" => TabCompletionStrings.DescendingHashtableKeyDescription, + "EndTime" => TabCompletionStrings.EndTimeHashtableKeyDescription, "Expression" => TabCompletionStrings.ExpressionHashtableKeyDescription, "FormatString" => TabCompletionStrings.FormatStringHashtableKeyDescription, "ID" => TabCompletionStrings.IDHashtableKeyDescription, @@ -8535,6 +8537,9 @@ private static List GetSpecialHashTableKeyMembers(HashSet TabCompletionStrings.NameHashtableKeyDescription, "Path" => TabCompletionStrings.PathHashtableKeyDescription, "ProviderName" => TabCompletionStrings.ProviderNameHashtableKeyDescription, + "StartTime" => TabCompletionStrings.StartTimeHashtableKeyDescription, + "SuppressHashFilter" => TabCompletionStrings.SuppressHashFilterHashtableKeyDescription, + "UserID" => TabCompletionStrings.UserIDHashtableKeyDescription, "Width" => TabCompletionStrings.WidthHashtableKeyDescription, _ => string.Empty }; From cb2474b7a605330e47eae82532753e0c8d7ddf8d Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Mon, 24 Mar 2025 17:09:08 +0100 Subject: [PATCH 10/10] Address feedback --- .../resources/TabCompletionStrings.resx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/resources/TabCompletionStrings.resx b/src/System.Management.Automation/resources/TabCompletionStrings.resx index 7bb2f666e8a..c201facc3e2 100644 --- a/src/System.Management.Automation/resources/TabCompletionStrings.resx +++ b/src/System.Management.Automation/resources/TabCompletionStrings.resx @@ -357,15 +357,15 @@ The value must be greater than 0. [int] -The depth key allows you to specify the depth of expansion per property. +The depth key specifies the depth of expansion per property. [bool] -Allows you to specify the order of sorting for one or more properties. +Specifies the order of sorting for one or more properties. [bool] -Allows you to specify the order of sorting for one or more properties. +Specifies the order of sorting for one or more properties. [String[]] @@ -403,7 +403,7 @@ Selects events with the specified event IDs. [int[]] Selects events with the specified log levels. -You can use the following log levels: +The following log levels are valid: 1: Critical 2: Error 3: Warning