diff --git a/NTFSSecurity/EnumArgumentCompleters.cs b/NTFSSecurity/EnumArgumentCompleters.cs
index 87604d6..927d1ea 100644
--- a/NTFSSecurity/EnumArgumentCompleters.cs
+++ b/NTFSSecurity/EnumArgumentCompleters.cs
@@ -13,21 +13,40 @@ namespace NTFSSecurity
internal static class EnumArgumentCompletion
{
///
- /// Returns the names of the enumeration that start with the word the user already typed, compared without
- /// regard to case, in the order Enum.GetNames returns them.
+ /// Returns the names of the enumeration that match the word the user already typed, compared without
+ /// regard to case, in the order Enum.GetNames returns them. A leading quote is stripped before matching,
+ /// together with the matching trailing quote that PowerShell adds to a word it completes while the quote
+ /// is still open, and every match is returned quoted the same way, so that completing inside an open quote
+ /// keeps the command line valid. The word itself may already contain a wildcard, such as "*Only" or "D*",
+ /// the same as PowerShell completes a parameter of an enumeration type.
///
/// The enumeration to complete the names of.
/// The word the user already typed; empty or null completes every name.
internal static IEnumerable Complete(Type enumType, string wordToComplete)
{
var word = wordToComplete ?? string.Empty;
+ char? quote = null;
+
+ if (word.Length > 0 && (word[0] == '\'' || word[0] == '"'))
+ {
+ quote = word[0];
+ word = word.Substring(1);
+
+ if (word.Length > 0 && word[word.Length - 1] == quote.Value)
+ {
+ word = word.Substring(0, word.Length - 1);
+ }
+ }
+
+ var pattern = new WildcardPattern(word + "*", WildcardOptions.IgnoreCase);
var results = new List();
foreach (var name in Enum.GetNames(enumType))
{
- if (name.StartsWith(word, StringComparison.OrdinalIgnoreCase))
+ if (pattern.IsMatch(name))
{
- results.Add(new CompletionResult(name, name, CompletionResultType.ParameterValue, name));
+ var completionText = quote.HasValue ? quote.Value + name + quote.Value : name;
+ results.Add(new CompletionResult(completionText, name, CompletionResultType.ParameterValue, name));
}
}
diff --git a/Tests/ParameterSets.Access.Tests.ps1 b/Tests/ParameterSets.Access.Tests.ps1
index c3fd1c9..11189f1 100644
--- a/Tests/ParameterSets.Access.Tests.ps1
+++ b/Tests/ParameterSets.Access.Tests.ps1
@@ -1420,6 +1420,17 @@ Describe 'Invalid and valid values of AccessType, InheritanceFlags, PropagationF
@{ Cmdlet = $cmdlet; Parameter = 'PropagationFlags'; EnumTypeName = 'System.Security.AccessControl.PropagationFlags' }
@{ Cmdlet = $cmdlet; Parameter = 'AppliesTo'; EnumTypeName = 'Security2.ApplyTo' }
}
+
+ # R1-4: a quoted or a wildcard word completed in rc8 (a typed parameter of an enumeration); the first
+ # EnumArgumentCompleters.cs returned nothing for either shape. Each ExpectedName has no sibling name that
+ # also starts with it (ThisFolderOnly has no ThisFolderOnlyOneLevel), so the unquoted and quoted forms match
+ # it alone; the wildcard only needs to contain it among the matches.
+ $accessCompletionFormCases = @(
+ @{ Parameter = 'AccessType'; Word = 'D'; ExpectedName = 'Deny' }
+ @{ Parameter = 'InheritanceFlags'; Word = 'Container'; ExpectedName = 'ContainerInherit' }
+ @{ Parameter = 'PropagationFlags'; Word = 'Inh'; ExpectedName = 'InheritOnly' }
+ @{ Parameter = 'AppliesTo'; Word = 'ThisFolderO'; ExpectedName = 'ThisFolderOnly' }
+ )
}
Context 'A bad value bound from the pipeline' {
@@ -1753,6 +1764,26 @@ Describe 'Invalid and valid values of AccessType, InheritanceFlags, PropagationF
It ' should declare - as [object]' -ForEach $accessCompleterCases {
(Get-Command -Name $Cmdlet).Parameters[$Parameter].ParameterType | Should -Be ([object])
}
+
+ It 'Should complete Add-NTFSAccess - for the unquoted, single-quoted, double-quoted, and wildcard forms of ' -ForEach $accessCompletionFormCases {
+ $base = 'Add-NTFSAccess -Path C:\x -Account a -AccessRights Read -{0} ' -f $Parameter
+
+ $unquoted = $base + $Word
+ $r1 = [System.Management.Automation.CommandCompletion]::CompleteInput($unquoted, $unquoted.Length, $null)
+ @($r1.CompletionMatches.CompletionText) | Should -Contain $ExpectedName
+
+ $singleQuoted = $base + "'" + $Word
+ $r2 = [System.Management.Automation.CommandCompletion]::CompleteInput($singleQuoted, $singleQuoted.Length, $null)
+ @($r2.CompletionMatches.CompletionText) | Should -Contain ("'" + $ExpectedName + "'")
+
+ $doubleQuoted = $base + '"' + $Word
+ $r3 = [System.Management.Automation.CommandCompletion]::CompleteInput($doubleQuoted, $doubleQuoted.Length, $null)
+ @($r3.CompletionMatches.CompletionText) | Should -Contain ('"' + $ExpectedName + '"')
+
+ $wildcard = $base + '*' + $ExpectedName.Substring($ExpectedName.Length - 3)
+ $r4 = [System.Management.Automation.CommandCompletion]::CompleteInput($wildcard, $wildcard.Length, $null)
+ @($r4.CompletionMatches.CompletionText) | Should -Contain $ExpectedName
+ }
}
}
diff --git a/Tests/ParameterSets.Audit.Tests.ps1 b/Tests/ParameterSets.Audit.Tests.ps1
index fb2cb0d..f3c6868 100644
--- a/Tests/ParameterSets.Audit.Tests.ps1
+++ b/Tests/ParameterSets.Audit.Tests.ps1
@@ -1092,6 +1092,13 @@ Describe 'Invalid and valid values of AuditFlags, InheritanceFlags, PropagationF
@{ Cmdlet = $cmdlet; Parameter = 'PropagationFlags'; EnumTypeName = 'System.Security.AccessControl.PropagationFlags' }
@{ Cmdlet = $cmdlet; Parameter = 'AppliesTo'; EnumTypeName = 'Security2.ApplyTo' }
}
+
+ # R1-4: AuditFlags is the one completer that ParameterSets.Access.Tests.ps1 does not already cover with the
+ # quoted and wildcard forms (it shares the same completer code for InheritanceFlags, PropagationFlags, and
+ # AppliesTo, so those are pinned there).
+ $auditCompletionFormCases = @(
+ @{ Parameter = 'AuditFlags'; Word = 'F'; ExpectedName = 'Failure' }
+ )
}
Context 'A bad value bound from the pipeline' {
@@ -1413,5 +1420,25 @@ Describe 'Invalid and valid values of AuditFlags, InheritanceFlags, PropagationF
It ' should declare - as [object]' -ForEach $auditCompleterCases {
(Get-Command -Name $Cmdlet).Parameters[$Parameter].ParameterType | Should -Be ([object])
}
+
+ It 'Should complete Add-NTFSAudit - for the unquoted, single-quoted, double-quoted, and wildcard forms of ' -ForEach $auditCompletionFormCases {
+ $base = 'Add-NTFSAudit -Path C:\x -Account a -AccessRights Read -{0} ' -f $Parameter
+
+ $unquoted = $base + $Word
+ $r1 = [System.Management.Automation.CommandCompletion]::CompleteInput($unquoted, $unquoted.Length, $null)
+ @($r1.CompletionMatches.CompletionText) | Should -Contain $ExpectedName
+
+ $singleQuoted = $base + "'" + $Word
+ $r2 = [System.Management.Automation.CommandCompletion]::CompleteInput($singleQuoted, $singleQuoted.Length, $null)
+ @($r2.CompletionMatches.CompletionText) | Should -Contain ("'" + $ExpectedName + "'")
+
+ $doubleQuoted = $base + '"' + $Word
+ $r3 = [System.Management.Automation.CommandCompletion]::CompleteInput($doubleQuoted, $doubleQuoted.Length, $null)
+ @($r3.CompletionMatches.CompletionText) | Should -Contain ('"' + $ExpectedName + '"')
+
+ $wildcard = $base + '*' + $ExpectedName.Substring($ExpectedName.Length - 3)
+ $r4 = [System.Management.Automation.CommandCompletion]::CompleteInput($wildcard, $wildcard.Length, $null)
+ @($r4.CompletionMatches.CompletionText) | Should -Contain $ExpectedName
+ }
}
}