Building PowerShell auto-completion using TabExpansionPlusPlus – Advanced – Part 1


A couple of weeks ago, we covered in a series of blog posts the “argument completion” using TabExpansionPlusPlus. 

In this upcoming series of blog posts, we will discuss some more advanced topics relating to “argument completion”.

Supporting the context of other parameters for argument completion

When automatic Intellisense is triggered, you may want to take other parameters into account and provide arguments in the context of those specified parameters.
For example:

Get-HotFix –Id <TAB>
# Show hotfixes for the computerX Get-HotFix –Computer <ComputerX> –Id <TAB>
# Show hotfixes for the computerY Get-HotFix –Computer <ComputerY> –Id <TAB>

Depending on the specified parameter -ComputerName, you want your argument completer to adapt to that parameter value.
We can use the variable $fakeBoundParameter for that and combine it with some PowerShell splatting.

Here’s how:

function HotFix_IdArgumentCompletion
{
  param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

  $OptionalParams = @{}
  $ComputerName = $fakeBoundParameter["ComputerName"]
  if($ComputerName) { $OptionalParams.ComputerName = $ComputerName }


  Microsoft.PowerShell.Management\Get-HotFix @OptionalParams |
        Where-Object {$_.HotFixId -like "$wordToComplete*"} |
        Sort-Object -Property HotFixId |
        ForEach-Object {             
                $CompletionText = '{0}' -f $_.HotFixId
                $ToolTip = '{0}' -f $_.Description
                $ListItemText = '{0}' -f $_.HotFixId

                New-CompletionResult -CompletionText $CompletionText -ListItemText $ListItemText -ToolTip $ToolTip
        }
}

Register-ArgumentCompleter `
    -CommandName 'Get-HotFix' `
    -ParameterName 'Id' `
    -ScriptBlock $function:HotFix_IdArgumentCompletion

Increasing the performance of custom argument completers using Argument Caching

By default, automatic Intellisense in the ISE times out after 500ms, so if your completer runs slowly, it may not be useful for Intellisense.
Sometimes it is possible to cache data to speed up your completer.  For example: doing argument completing for hotfix Ids using Get-HotFix -Id.

TabExpansionPlusPlus provides a couple of useful utilities to help.
There are two functions to give you a simple way to save and retrieve our cached data:

  • Set-CompletionPrivateData
  • Get-CompletionPrivateData

These functions are simple wrappers around a hashtable $CompletionPrivateData that is kept in the TabExpansionPlusPlus module.
You should prefer these functions over using global variables if your completer is defined in a script.

One strategy to caching is to build the cache the first time your completer runs.
This is memory efficient, but if the cache takes a long time to build, you might not get any Intellisense the first time around or worse, the pipeline might get stopped and your completer never gets a chance to save the expensive work it did to build the cache.

The hashtable $CompletionPrivateData will have entries for each argument completion that runs and each entry has an expiration date/time which acts as a TTL (Time To Live). image
At first time, your argument completer runs (probably slowly) and populates the cache.
From than on, you are being served from the cache (as long as the time to live is valid).

Example:

function HotFix_IdArgumentCompletion
{
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

    $OptionalParams = @{}
    $ComputerName = $fakeBoundParameter["ComputerName"]
    if($ComputerName) { $OptionalParams.ComputerName = $ComputerName }



    $CacheKey = if($ComputerName) { "HotFix_$ComputerName" } else { "HotFix" }
    $Cache = Get-CompletionPrivateData -Key $CacheKey
    if ($Cache) {
        $List = $Cache
    } else {
       $List = Microsoft.PowerShell.Management\Get-HotFix @OptionalParams
        Set-CompletionPrivateData -Key $CacheKey -Value $List -ExpirationSeconds 300 # Expiration: 5 min
    }   

$List |
        Where-Object {$_.HotFixId -like "$wordToComplete*"} |
        Sort-Object -Property HotFixId |
        ForEach-Object {             
                $CompletionText = '{0}' -f $_.HotFixId
                $ToolTip = '{0}' -f $_.Description
                $ListItemText = '{0}' -f $_.HotFixId
                $CompletionResultType = [System.Management.Automation.CompletionResultType]::ParameterValue                 New-CompletionResult -CompletionText $CompletionText -ListItemText $ListItemText -ToolTip $ToolTip
        }
}

In the above code, for slow auto-completers, we store the retrieved argument values (here: the list of hotfixes) in a cache and set an expiration time on them (here: 5 min).
These argument values serve as the basis for creating completion results using New-CompletionResult.
The next time the same argument completion is invoked, we check if the requested data is already present in the cache (using $Cache = Get-CompletionPrivateData –Key $CacheKey).
If so, we use it to speed up the argument completion process and make our slow argument completer become a fast argument completer. Smile
If not, we populate the cache with a defined time stamp using Set-CompletionPrivateData.

When you now perform the same argument completion for Get-HotFix again, the performance improvement is significant when triggering the argument completion from the second time on (when being served from the cached).

Get-HotFix –Id <TAB>
Get-HotFix –Computer <ComputerX> –Id <TAB>
Get-HotFix –Computer <ComputerY> –Id <TAB>

Finding the right expiration time (TTL) is dependent on what argument completion scenario you are dealing with…

Due to a recent change in version 1.2 of TabExpansionPlusPlus, you are able to see the validity of the cache using Get-CompletionPrivateData.
image

Hope this helps…

Advertisements

One thought on “Building PowerShell auto-completion using TabExpansionPlusPlus – Advanced – Part 1

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s