How to get <tab> value suggestions dynamically, without throwing an error if user provided a value that does not exist?

How to get <tab> value suggestions dynamically, without throwing an error if user provided a value that does not exist?
typescript
Ethan Jackson

Lets say, I have two functions get-foo and new-foo, that I am using to read and edit a tree structure resource. Its really nothing sophisticated, I am using the file system to implement the structure.

The issue am having is get-foo works as I want it to, it will force the user to only input values that are found in the tree structure.

My issue is, new-foo is not working as I want it to, I would like values from the tree structure to be suggested similar to how they are with get-foo, but the user must be able to input arbitrary values, so they can extend the structure. Currently new-foo will throw an error if the value does not exist.

My code:

Function get-foo{ param( [ValidateSet([myTree], ErrorMessage = """{0}"" Is not a valid structure")] [string]$name ) $name } Function new-foo{ param( [ValidateSet([colNames])] [string]$name ) $name } Class myTree : System.Management.Automation.IValidateSetValuesGenerator{ [string[]] GetValidValues(){ return [string[]] (Get-ChildItem -path "C:\temp" -Recurse |ForEach-Object {($_.FullName -replace 'c:\\temp\\')}) }}

get-foo and new-foo both have a name parameter, the user is expected to provide a name. The functions check the directory c:\temp, for valid names.

For example, if c:temp was as follows:

C:\temp\animal C:\temp\animals\cat C:\temp\animals\dog C:\temp\animals\fish C:\temp\colours C:\temp\colours\green C:\temp\colours\orange C:\temp\colours\red C:\temp\plants C:\temp\plants\carrots C:\temp\plants\patato C:\temp\plants\vegatables

Then with get-foo -name anima...<tab>, then the auto competition examples would be:

  • get-foo -name animals\cat
  • get-foo -name animals\dog
  • get-foo -name animals\fish

But new-foo will throw an error if the value name does not already exist. Is there a mechanism that I can use to still get dynamic autocompletion but without the error? I checked the parameter attribute section, from my reading only validatSet seems applicable.

Am on pwsh 7.4

Answer

You can implement IArgumentCompleter in your existing class to suggest values but not validate them:

using namespace System using namespace System.Collections using namespace System.Collections.Generic using namespace System.Management.Automation using namespace System.Management.Automation.Language class MyTree : IValidateSetValuesGenerator, IArgumentCompleter { [string[]] GetValidValues() { return $this.GetValues() } [IEnumerable[CompletionResult]] CompleteArgument( [string] $commandName, [string] $parameterName, [string] $wordToComplete, [CommandAst] $commandAst, [IDictionary] $fakeBoundParameters) { $values = foreach ($value in $this.GetValues()) { [CompletionResult]::new($value) } return [CompletionResult[]] @($values) } hidden [string[]] GetValues() { return Get-ChildItem -Path 'c:\\temp\\' -Recurse | ForEach-Object { $_.FullName -replace 'c:\\temp\\' } } }

Then the implementation for Get-Foo remains the same, using [ValidateSet([MyTree], ...)] but for New-Foo you'd use [ArgumentCompleter(...)] instead:

function New-Foo { param( [ArgumentCompleter([MyTree])] [string] $Name ) }

Other alternative that doesn't require to implement IArgumentCompleter would be to use Register-ArgumentCompleter, you can find an example in this answer.

Related Articles