Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ public int Millisecond
/// Unix format string
/// </summary>
[Parameter(ParameterSetName = "net")]
[ArgumentCompletions("FileDate", "FileDateUniversal", "FileDateTime", "FileDateTimeUniversal")]
public string Format { get; set; }

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2002,6 +2002,20 @@ private static void NativeCommandArgumentCompletion(
{
}
}

var argumentCompletionsAttribute = parameter.CompiledAttributes.OfType<ArgumentCompletionsAttribute>().FirstOrDefault();
if (argumentCompletionsAttribute != null)
{
var customResults = argumentCompletionsAttribute.CompleteArgument(commandName, parameterName,
context.WordToComplete, commandAst, GetBoundArgumentsAsHashtable(context));
if (customResults != null)
{
result.AddRange(customResults);
result.Add(CompletionResult.Null);
return;
}
}

switch (commandName)
{
case "Get-Command":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,4 +158,56 @@ protected override void EndProcessing()
}
}
}

/// <summary>
/// This attribute is used to specify an argument completions for a parameter of a cmdlet or function
/// based on string array.
/// <example>
/// [Parameter()]
/// [ArgumentCompletions("Option1","Option2","Option3")]
/// public string Noun { get; set; }
/// </example>
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ArgumentCompletionsAttribute : Attribute
{
private string[] _completions;

/// <summary>
/// Initializes a new instance of the ArgumentCompletionsAttribute class
/// </summary>
/// <param name="completions">list of complete values</param>
/// <exception cref="ArgumentNullException">for null arguments</exception>
/// <exception cref="ArgumentOutOfRangeException">for invalid arguments</exception>
public ArgumentCompletionsAttribute(params string[] completions)
{
if (completions == null)
{
throw PSTraceSource.NewArgumentNullException("completions");
}

if (completions.Length == 0)
{
throw PSTraceSource.NewArgumentOutOfRangeException("completions", completions);
}

_completions = completions;
}

/// <summary>
/// The function returns completions for arguments.
/// </summary>
public IEnumerable<CompletionResult> CompleteArgument(string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters)
{
var wordToCompletePattern = WildcardPattern.Get(string.IsNullOrWhiteSpace(wordToComplete) ? "*" : wordToComplete + "*", WildcardOptions.IgnoreCase);

foreach (var str in _completions)
{
if (wordToCompletePattern.IsMatch(str))
{
yield return new CompletionResult(str, str, CompletionResultType.ParameterValue, str);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,7 @@ internal static class CoreTypes
{ typeof(AllowEmptyStringAttribute), new[] { "AllowEmptyString" } },
{ typeof(AllowNullAttribute), new[] { "AllowNull" } },
{ typeof(ArgumentCompleterAttribute), new[] { "ArgumentCompleter" } },
{ typeof(ArgumentCompletionsAttribute), new[] { "ArgumentCompletions" } },
{ typeof(Array), new[] { "array" } },
{ typeof(bool), new[] { "bool" } },
{ typeof(byte), new[] { "byte" } },
Expand Down
94 changes: 94 additions & 0 deletions test/powershell/Language/Parser/ExtensibleCompletion.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -355,3 +355,97 @@ Describe "Additional type name completion tests" -Tags "CI" {
TestInput = 'Get-Command -ParameterType System.Collections.Generic.Dic'
} | Get-CompletionTestCaseData | Test-Completions
}

Describe "ArgumentCompletionsAttribute tests" -Tags "CI" {

BeforeAll {
function TestArgumentCompletionsAttribute
{
param(
[ArgumentCompletions("value1", "value2", "value3")]
$Alpha,
$Beta
)
}

function TestArgumentCompletionsAttribute1
{
param(
[ArgumentCompletionsAttribute("value1", "value2", "value3")]
$Alpha,
$Beta
)
}

$cmdletSrc=@'
using System;
using System.Management.Automation;
using System.Collections.Generic;

namespace Test.A {

[Cmdlet(VerbsCommon.Get, "ArgumentCompletions")]
public class TestArgumentCompletionsAttributeCommand : PSCmdlet
{
[Parameter]
[ArgumentCompletions("value1", "value2", "value3")]
public string Param1;

protected override void EndProcessing()
{
WriteObject(Param1);
}
}

[Cmdlet(VerbsCommon.Get, "ArgumentCompletions1")]
public class TestArgumentCompletionsAttributeCommand1 : PSCmdlet
{
[Parameter]
[ArgumentCompletionsAttribute("value1", "value2", "value3")]
public string Param1;

protected override void EndProcessing()
{
WriteObject(Param1);
}
}
}
'@
$cls = Add-Type -TypeDefinition $cmdletSrc -PassThru | Select-Object -First 1
$testModule = Import-Module $cls.Assembly -PassThru

$testCasesScript = @(
@{ attributeName = "ArgumentCompletions" ; cmdletName = "TestArgumentCompletionsAttribute" },
@{ attributeName = "ArgumentCompletionsAttribute"; cmdletName = "TestArgumentCompletionsAttribute1" }
)

$testCasesCSharp = @(
@{ attributeName = "ArgumentCompletions" ; cmdletName = "Get-ArgumentCompletions" },
@{ attributeName = "ArgumentCompletionsAttribute"; cmdletName = "Get-ArgumentCompletions1" }
)
}

AfterAll {
Remove-Module -ModuleInfo $testModule
}

It "<attributeName> works in script" -TestCases $testCasesScript {
param($attributeName, $cmdletName)

$line = "$cmdletName -Alpha val"
$res = TaBexpansion2 -inputScript $line -cursorColumn $line.Length
$res.CompletionMatches.Count | Should Be 3
$res.CompletionMatches.CompletionText -join " " | Should Be "value1 value2 value3"
{ TestArgumentCompletionsAttribute -Alpha unExpectedValue } | Should Not Throw
}

It "<attributeName> works in C#" -TestCases $testCasesCSharp {
param($attributeName, $cmdletName)

$line = "$cmdletName -Param1 val"
$res = TaBexpansion2 -inputScript $line -cursorColumn $line.Length
$res.CompletionMatches.Count | Should Be 3
$res.CompletionMatches.CompletionText -join " " | Should Be "value1 value2 value3"
{ TestArgumentCompletionsAttribute -Param1 unExpectedValue } | Should Not Throw
}
}
6 changes: 5 additions & 1 deletion test/powershell/Language/Parser/TypeAccelerator.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ Describe "Type accelerators" -Tags "CI" {
Accelerator = 'ArgumentCompleter'
Type = [System.Management.Automation.ArgumentCompleterAttribute]
}
@{
Accelerator = 'ArgumentCompletions'
Type = [System.Management.Automation.ArgumentCompletionsAttribute]
}
@{
Accelerator = 'array'
Type = [System.Array]
Expand Down Expand Up @@ -368,7 +372,7 @@ Describe "Type accelerators" -Tags "CI" {

if ( $IsCoreCLR )
{
$totalAccelerators = 89
$totalAccelerators = 90
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,34 +81,49 @@ Describe "Get-Date DRT Unit Tests" -Tags "CI" {


Describe "Get-Date" -Tags "CI" {
It "-Format FileDate works" {
Get-date -Date 0030-01-01T01:02:03.0004 -Format FileDate | Should Be "00300101"
}

It "-Format FileDateTime works" {
Get-date -Date 0030-01-01T01:02:03.0004 -Format FileDateTime | Should Be "00300101T0102030004"
}

It "-Format FileDateTimeUniversal works" {
Get-date -Date 0030-01-01T01:02:03.0004z -Format FileDateTimeUniversal | Should Be "00300101T0102030004Z"
}

It "-Format FileDateTimeUniversal works" {
Get-date -Date 0030-01-01T01:02:03.0004z -Format FileDateUniversal | Should Be "00300101Z"
}

It "Should have colons when ToString method is used" {
(Get-Date).ToString().Contains(":") | Should be $true
(Get-Date -DisplayHint Time).ToString().Contains(":") | Should be $true
(Get-Date -DisplayHint Date).ToString().Contains(":") | Should be $true
(Get-Date).ToString().Contains(":") | Should be $true
(Get-Date -DisplayHint Time).ToString().Contains(":") | Should be $true
(Get-Date -DisplayHint Date).ToString().Contains(":") | Should be $true
}

It "Should be able to use the format flag" {
# You would think that one could use simple loops here, but apparently powershell in Windows returns different values in loops

(Get-Date -Format d).Contains("/") | Should be $true
(Get-Date -Format D).Contains(",") | Should be $true
(Get-Date -Format f).Contains(",") -and (Get-Date -Format f).Contains(":") | Should be $true
(Get-Date -Format F).Contains(",") -and (Get-Date -Format F).Contains(":") | Should be $true
(Get-Date -Format g).Contains("/") -and (Get-Date -Format g).Contains(":") | Should be $true
(Get-Date -Format G).Contains("/") -and (Get-Date -Format G).Contains(":") | Should be $true
(Get-Date -Format m).Contains(",") -or `
(Get-Date -Format m).Contains(":") -or `
(Get-Date -Format m).Contains("/") | Should be $false
# You would think that one could use simple loops here, but apparently powershell in Windows returns different values in loops

(Get-Date -Format d).Contains("/") | Should be $true
(Get-Date -Format D).Contains(",") | Should be $true
(Get-Date -Format f).Contains(",") -and (Get-Date -Format f).Contains(":") | Should be $true
(Get-Date -Format F).Contains(",") -and (Get-Date -Format F).Contains(":") | Should be $true
(Get-Date -Format g).Contains("/") -and (Get-Date -Format g).Contains(":") | Should be $true
(Get-Date -Format G).Contains("/") -and (Get-Date -Format G).Contains(":") | Should be $true
(Get-Date -Format m).Contains(",") -or `
(Get-Date -Format m).Contains(":") -or `
(Get-Date -Format m).Contains("/") | Should be $false
}

It "Should check that Get-Date can return the correct datetime from the system time" {
$timeDifference = $(Get-Date).Subtract([System.DateTime]::Now)
$timeDifference = $(Get-Date).Subtract([System.DateTime]::Now)

$timeDifference.Days | Should Be 0
$timeDifference.Hours | Should Be 0
$timeDifference.Minutes | Should Be 0
$timeDifference.Milliseconds | Should BeLessThan 1
$timeDifference.Ticks | Should BeLessThan 10000
$timeDifference.Days | Should Be 0
$timeDifference.Hours | Should Be 0
$timeDifference.Minutes | Should Be 0
$timeDifference.Milliseconds | Should BeLessThan 1
$timeDifference.Ticks | Should BeLessThan 10000
}

}