mirror of
https://github.com/PowerShell/PowerShell.git
synced 2024-11-27 11:56:14 +08:00
Adding IArgumentCompleterFactory
for parameterized ArgumentCompleters
(#12605)
This commit is contained in:
parent
5d0dadf19d
commit
be7d36603d
@ -2030,19 +2030,17 @@ namespace System.Management.Automation
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argumentCompleterAttribute.Type != null)
|
||||
var completer = argumentCompleterAttribute.CreateArgumentCompleter();
|
||||
|
||||
if (completer != null)
|
||||
{
|
||||
var completer = Activator.CreateInstance(argumentCompleterAttribute.Type) as IArgumentCompleter;
|
||||
if (completer != null)
|
||||
var customResults = completer.CompleteArgument(commandName, parameterName,
|
||||
context.WordToComplete, commandAst, GetBoundArgumentsAsHashtable(context));
|
||||
if (customResults != null)
|
||||
{
|
||||
var customResults = completer.CompleteArgument(commandName, parameterName,
|
||||
context.WordToComplete, commandAst, GetBoundArgumentsAsHashtable(context));
|
||||
if (customResults != null)
|
||||
{
|
||||
result.AddRange(customResults);
|
||||
result.Add(CompletionResult.Null);
|
||||
return;
|
||||
}
|
||||
result.AddRange(customResults);
|
||||
result.Add(CompletionResult.Null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -40,19 +40,40 @@ namespace System.Management.Automation
|
||||
Type = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentCompleterAttribute"/> class.
|
||||
/// This constructor is used by derived attributes implementing <see cref="IArgumentCompleterFactory"/>.
|
||||
/// </summary>
|
||||
protected ArgumentCompleterAttribute()
|
||||
{
|
||||
if (this is not IArgumentCompleterFactory)
|
||||
{
|
||||
throw PSTraceSource.NewInvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This constructor is used primarily via PowerShell scripts.
|
||||
/// </summary>
|
||||
/// <param name="scriptBlock"></param>
|
||||
public ArgumentCompleterAttribute(ScriptBlock scriptBlock)
|
||||
{
|
||||
if (scriptBlock == null)
|
||||
if (scriptBlock is null)
|
||||
{
|
||||
throw PSTraceSource.NewArgumentNullException(nameof(scriptBlock));
|
||||
}
|
||||
|
||||
ScriptBlock = scriptBlock;
|
||||
}
|
||||
|
||||
internal IArgumentCompleter CreateArgumentCompleter()
|
||||
{
|
||||
return Type != null
|
||||
? Activator.CreateInstance(Type) as IArgumentCompleter
|
||||
: this is IArgumentCompleterFactory factory
|
||||
? factory.Create()
|
||||
: null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -85,6 +106,67 @@ namespace System.Management.Automation
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new argument completer.
|
||||
/// </summary>
|
||||
/// <para>
|
||||
/// If an attribute that derives from <see cref="ArgumentCompleterAttribute"/> implements this interface,
|
||||
/// it will be used to create the <see cref="IArgumentCompleter"/>, thus giving a way to parameterize a completer.
|
||||
/// The derived attribute can have properties or constructor arguments that are used when creating the completer.
|
||||
/// </para>
|
||||
/// <example>
|
||||
/// This example shows the intended usage of <see cref="IArgumentCompleterFactory"/> to pass arguments to an argument completer.
|
||||
/// <code>
|
||||
/// public class NumberCompleterAttribute : ArgumentCompleterAttribute, IArgumentCompleterFactory {
|
||||
/// private readonly int _from;
|
||||
/// private readonly int _to;
|
||||
///
|
||||
/// public NumberCompleterAttribute(int from, int to){
|
||||
/// _from = from;
|
||||
/// _to = to;
|
||||
/// }
|
||||
///
|
||||
/// // use the attribute parameters to create a parameterized completer
|
||||
/// IArgumentCompleter Create() => new NumberCompleter(_from, _to);
|
||||
/// }
|
||||
///
|
||||
/// class NumberCompleter : IArgumentCompleter {
|
||||
/// private readonly int _from;
|
||||
/// private readonly int _to;
|
||||
///
|
||||
/// public NumberCompleter(int from, int to){
|
||||
/// _from = from;
|
||||
/// _to = to;
|
||||
/// }
|
||||
///
|
||||
/// IEnumerable{CompletionResult} CompleteArgument(string commandName, string parameterName, string wordToComplete,
|
||||
/// CommandAst commandAst, IDictionary fakeBoundParameters) {
|
||||
/// for(int i = _from; i < _to; i++) {
|
||||
/// yield return new CompletionResult(i.ToString());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public interface IArgumentCompleterFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an instance of a class implementing the <see cref="IArgumentCompleter"/> interface.
|
||||
/// </summary>
|
||||
/// <returns>An IArgumentCompleter instance.</returns>
|
||||
IArgumentCompleter Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for parameterized argument completer attributes.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public abstract class ArgumentCompleterFactoryAttribute : ArgumentCompleterAttribute, IArgumentCompleterFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public abstract IArgumentCompleter Create();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsLifecycle.Register, "ArgumentCompleter", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=528576")]
|
||||
|
@ -175,6 +175,77 @@ function TestFunction
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class NumberCompleter : IArgumentCompleter
|
||||
{
|
||||
|
||||
[int] $From
|
||||
[int] $To
|
||||
[int] $Step
|
||||
|
||||
NumberCompleter([int] $from, [int] $to, [int] $step)
|
||||
{
|
||||
if ($from -gt $to) {
|
||||
throw [ArgumentOutOfRangeException]::new("from")
|
||||
}
|
||||
$this.From = $from
|
||||
$this.To = $to
|
||||
$this.Step = if($step -lt 1) { 1 } else { $step }
|
||||
}
|
||||
|
||||
[IEnumerable[CompletionResult]] CompleteArgument(
|
||||
[string] $CommandName,
|
||||
[string] $parameterName,
|
||||
[string] $wordToComplete,
|
||||
[CommandAst] $commandAst,
|
||||
[IDictionary] $fakeBoundParameters)
|
||||
{
|
||||
$resultList = [List[CompletionResult]]::new()
|
||||
$local:to = $this.To
|
||||
for ($i = $this.From; $i -le $to; $i += $this.Step) {
|
||||
if ($i.ToString().StartsWith($wordToComplete, [System.StringComparison]::Ordinal)) {
|
||||
$num = $i.ToString()
|
||||
$resultList.Add([CompletionResult]::new($num, $num, "ParameterValue", $num))
|
||||
}
|
||||
}
|
||||
|
||||
return $resultList
|
||||
}
|
||||
}
|
||||
|
||||
class NumberCompletionAttribute : ArgumentCompleterAttribute, IArgumentCompleterFactory
|
||||
{
|
||||
[int] $From
|
||||
[int] $To
|
||||
[int] $Step
|
||||
|
||||
NumberCompletionAttribute([int] $from, [int] $to)
|
||||
{
|
||||
$this.From = $from
|
||||
$this.To = $to
|
||||
$this.Step = 1
|
||||
}
|
||||
|
||||
[IArgumentCompleter] Create() { return [NumberCompleter]::new($this.From, $this.To, $this.Step) }
|
||||
}
|
||||
|
||||
function FactoryCompletionAdd {
|
||||
param(
|
||||
[NumberCompletion(0, 50, Step = 5)]
|
||||
[int] $Number
|
||||
)
|
||||
}
|
||||
|
||||
Describe "Factory based extensible completion" -Tags "CI" {
|
||||
@{
|
||||
ExpectedResults = @(
|
||||
@{CompletionText = "5"; ResultType = "ParameterValue" }
|
||||
@{CompletionText = "50"; ResultType = "ParameterValue" }
|
||||
)
|
||||
TestInput = 'FactoryCompletionAdd -Number 5'
|
||||
} | Get-CompletionTestCaseData | Test-Completions
|
||||
}
|
||||
|
||||
Describe "Script block based extensible completion" -Tags "CI" {
|
||||
@{
|
||||
ExpectedResults = @(
|
||||
|
Loading…
Reference in New Issue
Block a user