mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Add quote handling in Verb, StrictModeVersion, Scope & PropertyType Argument Completers with single helper method (#24839)
This commit is contained in:
+7
-25
@@ -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)
|
||||
: [];
|
||||
|
||||
/// <summary>
|
||||
/// Checks if parameter paths are from Registry provider.
|
||||
|
||||
@@ -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 <path> -Verb <wordToComplete>
|
||||
@@ -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 [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completes file verbs.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file path to get verbs.</param>
|
||||
/// <param name="wordToComplete">The word to complete.</param>
|
||||
/// <param name="filePath">The file path to get verbs.</param>
|
||||
/// <returns>List of file verbs to complete.</returns>
|
||||
private static IEnumerable<string> 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<CompletionResult> CompleteFileVerbs(string wordToComplete, string filePath)
|
||||
=> CompletionCompleters.GetMatchingResults(
|
||||
wordToComplete,
|
||||
possibleCompletionValues: new ProcessStartInfo(filePath).Verbs);
|
||||
}
|
||||
|
||||
#if !UNIX
|
||||
|
||||
@@ -8382,6 +8382,45 @@ namespace System.Management.Automation
|
||||
return quote;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get matching completions from word to complete.
|
||||
/// This makes it easier to handle different variations of completions with consideration of quotes.
|
||||
/// </summary>
|
||||
/// <param name="wordToComplete">The word to complete.</param>
|
||||
/// <param name="possibleCompletionValues">The possible completion values to iterate.</param>
|
||||
/// <param name="toolTipMapping">The optional tool tip mapping delegate.</param>
|
||||
/// <param name="resultType">The optional completion result type. Default is Text.</param>
|
||||
/// <returns></returns>
|
||||
internal static IEnumerable<CompletionResult> GetMatchingResults(
|
||||
string wordToComplete,
|
||||
IEnumerable<string> possibleCompletionValues,
|
||||
Func<string, string> 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)
|
||||
|
||||
+3
-11
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1332,35 +1332,6 @@ namespace System.Management.Automation
|
||||
/// <returns>Verb group display name.</returns>
|
||||
private static string GetVerbGroupDisplayName(Type verbType) => verbType.Name.Substring(5);
|
||||
|
||||
/// <summary>
|
||||
/// Filters by verbs and commands.
|
||||
/// </summary>
|
||||
/// <param name="verbs">The array of verbs.</param>
|
||||
/// <param name="commands">The collection of commands.</param>
|
||||
/// <returns>List of Verbs.</returns>
|
||||
private static IEnumerable<string> FilterByVerbsAndCommands(string[] verbs, Collection<CmdletInfo> commands)
|
||||
{
|
||||
if (commands is null || commands.Count == 0)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
Collection<WildcardPattern> verbPatterns = SessionStateUtilities.CreateWildcardsFromStrings(
|
||||
verbs,
|
||||
WildcardOptions.IgnoreCase);
|
||||
|
||||
foreach (CmdletInfo command in commands)
|
||||
{
|
||||
if (SessionStateUtilities.MatchesAnyWildcardPattern(
|
||||
command.Verb,
|
||||
verbPatterns,
|
||||
defaultValue: false))
|
||||
{
|
||||
yield return command.Verb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters by verbs and groups.
|
||||
/// </summary>
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if verb type exists in list of groups.
|
||||
/// </summary>
|
||||
/// <param name="groups">The list of groups</param>
|
||||
/// <param name="verbType">The verb type to check.</param>
|
||||
/// <returns>True if verb type was found, False if not found.</returns>
|
||||
private static bool GroupsContainVerbType(string[] groups, Type verbType)
|
||||
=> SessionStateUtilities.CollectionContainsValue(
|
||||
groups,
|
||||
GetVerbGroupDisplayName(verbType),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates field names from a Verb Type.
|
||||
/// </summary>
|
||||
/// <param name="verbType">The verb type.</param>
|
||||
/// <returns>List of field names.</returns>
|
||||
private static IEnumerable<string> EnumerateFieldNamesFromVerbType(Type verbType)
|
||||
{
|
||||
foreach (FieldInfo field in verbType.GetFields())
|
||||
{
|
||||
if (field.IsLiteral)
|
||||
{
|
||||
yield return field.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates field names from all Verb Types.
|
||||
/// </summary>
|
||||
/// <returns>List of field names.</returns>
|
||||
private static IEnumerable<string> EnumerateFieldNamesFromAllVerbTypes()
|
||||
{
|
||||
foreach (Type verbType in VerbTypes)
|
||||
{
|
||||
foreach (string fieldName in EnumerateFieldNamesFromVerbType(verbType))
|
||||
{
|
||||
yield return fieldName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates command verb names.
|
||||
/// </summary>
|
||||
/// <param name="commands">The collection of commands.</param>
|
||||
/// <returns>List of command verb names.</returns>
|
||||
private static IEnumerable<string> EnumerateCommandVerbNames(Collection<CmdletInfo> commands)
|
||||
{
|
||||
foreach (CmdletInfo command in commands)
|
||||
{
|
||||
yield return command.Verb;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters verbs by type.
|
||||
/// </summary>
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// Creates Verb info object from field info.
|
||||
/// </summary>
|
||||
/// <param name="field">The field.</param>
|
||||
/// <param name="fieldName">The field name.</param>
|
||||
/// <param name="verbType">The verb type.</param>
|
||||
/// <returns>VerbInfo object.</returns>
|
||||
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)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -1472,8 +1490,6 @@ namespace System.Management.Automation
|
||||
CommandAst commandAst,
|
||||
IDictionary fakeBoundParameters)
|
||||
{
|
||||
var verbs = new string[] { wordToComplete + "*" };
|
||||
|
||||
// Completion: Get-Verb -Group <group> -Verb <wordToComplete>
|
||||
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 <noun> -Verb <wordToComplete>
|
||||
@@ -1520,23 +1531,55 @@ namespace System.Management.Automation
|
||||
|
||||
Collection<CmdletInfo> commands = ps.Invoke<CmdletInfo>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completes verb with list of groups.
|
||||
/// </summary>
|
||||
/// <param name="wordToComplete">The word to complete.</param>
|
||||
/// <param name="groups">The list of groups.</param>
|
||||
/// <returns>List of completions for verb.</returns>
|
||||
private static IEnumerable<CompletionResult> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Completes verb with list of commands.
|
||||
/// </summary>
|
||||
/// <param name="wordToComplete">The word to complete.</param>
|
||||
/// <param name="commands">The list of commands.</param>
|
||||
/// <returns>List of completions for verb.</returns>
|
||||
private static IEnumerable<CompletionResult> CompleteVerbWithCommands(string wordToComplete, Collection<CmdletInfo> commands)
|
||||
=> CompletionCompleters.GetMatchingResults(
|
||||
wordToComplete,
|
||||
possibleCompletionValues: EnumerateCommandVerbNames(commands));
|
||||
|
||||
/// <summary>
|
||||
/// Completes verb for all types.
|
||||
/// </summary>
|
||||
/// <param name="wordToComplete">The word to complete.</param>
|
||||
/// <returns>List of completions for verb.</returns>
|
||||
private static IEnumerable<CompletionResult> CompleteVerbForAllTypes(string wordToComplete)
|
||||
=> CompletionCompleters.GetMatchingResults(
|
||||
wordToComplete,
|
||||
possibleCompletionValues: EnumerateFieldNamesFromAllVerbTypes());
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, bool> s_validVerbs = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
Reference in New Issue
Block a user