Adding IArgumentCompleterFactory for parameterized ArgumentCompleters by powercode · Pull Request #12605 · PowerShell/PowerShell · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 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 @@ -40,19 +40,40 @@ public ArgumentCompleterAttribute(Type type)
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>
Expand Down Expand Up @@ -83,6 +104,67 @@ IEnumerable<CompletionResult> CompleteArgument(
IDictionary fakeBoundParameters);
}

/// <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 &lt; _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")]
Expand Down
71 changes: 71 additions & 0 deletions test/powershell/Language/Parser/ExtensibleCompletion.Tests.ps1