Adding Argument Completion (Part 2)

by Jun 9, 2020

In the previous tip we talked about the new [ArgumentCompletions()] attribute that was added to PowerShell 7, and how you can use it to add sophisticated argument completion to your function parameters.

Unfortunately, this attribute isn’t available in Windows PowerShell, so by using it, your code is no longer compatible with Windows PowerShell.

Unless you add the attribute to Windows PowerShell, of course. When you run the code below in Windows PowerShell, the [ArgumentCompletions()] attribute becomes available. PowerShell 7 code stays compatible, and you can now start using this attribute in Windows PowerShell as well:

# are we running in Windows PowerShell?
if ($PSVersionTable.PSEdition -ne 'Core')
{
  # add the attribute [ArgumentCompletions()]
  $code = @'
using System;
using System.Collections.Generic;
using System.Management.Automation;

    public class ArgumentCompletionsAttribute : ArgumentCompleterAttribute
    {
        
        private static ScriptBlock _createScriptBlock(params string[] completions)
        {
            string text = "\"" + string.Join("\",\"", completions) + "\"";
            string code = "param($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams);@(" + text + ") -like \"*$WordToComplete*\" | Foreach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) }";
            return ScriptBlock.Create(code);
        }
        
        public ArgumentCompletionsAttribute(params string[] completions) : base(_createScriptBlock(completions))
        {
        }
    }
'@

  $null = Add-Type -TypeDefinition $code *>&1
}

As you see, the code only adds the new attribute if you run it in Windows PowerShell. In PowerShell 7, the attribute is already built-in.

Now you can use sophisticated argument completion in your functions regardless of whether you plan to use it in Windows PowerShell or Windows 7. Simply add the code above to your code to make sure the attribute is present.

Here is an example for a function that uses the attribute and provides argument completion:

function Get-Country
{
  param
  (
    # suggest country names:
    [ArgumentCompletions('USA','Germany','Norway','Sweden','Austria','YouNameIt')]
    [string]
    $Name
  )

  # return parameter
  $PSBoundParameters
}

When you run the code in PowerShell ISE (which is purely Windows PowerShell), and then use Get-Country in the interactive console, PowerShell ISE automatically suggests the argument values (country names) for the -Name parameter.

Here are two more things to consider:

  • Due to a long-standing bug in PowerShell, this type of argument completion does not work in the editor script pane where the actual function is defined. It always works in the interactive console (which is the most important use case), and any other script pane.
  • In contrast to the [ValidateSet()] attribute, the new [ArgumentCompletions()] attribute does not restrict the user input to the listed values. The new attribute just provides the suggestions you define but does not restrict user input in any way.

For more details on the techniques used here, visit https://powershell.one/powershell-internals/attributes/auto-completion.


Twitter This Tip! ReTweet this Tip!