I've tried 2 argument completers so far for my module to only show me the files with .cer
extension when I press TAB button.
Here is one of them
$ArgumentCompleterCertPath = {
Get-ChildItem | where-object { $_.extension -like '*.cer' } | foreach-object { return "`"$_`"" }
}
Register-ArgumentCompleter -CommandName "Edit-SignedWDACConfig" -ParameterName "CertPath" -ScriptBlock $ArgumentCompleterCertPath
And here is the other one
[ArgumentCompleter({
param($commandName, $parameterName, $wordToComplete, $commandAst)
# Get the current path from the command
$path = $commandAst.CommandElements |
Where-Object { $_.ParameterName -eq 'Path' } |
Select-Object -ExpandProperty Argument
# Resolve the path to a full path
$resolvedPath = Resolve-Path -LiteralPath $path
# Get the files with .cer extension in the resolved path
$files = Get-ChildItem -LiteralPath $resolvedPath -Filter *.cer
# Create an array of completion results from the file names
$completions = foreach ($file in $files) {
[System.Management.Automation.CompletionResult]::new($file.Name, $file.Name, 'ParameterValue', $file.Name)
}
# Return the completions
return $completions
})]
They both work when there is a certificate file in the current directory, but if there isn't any, they suggest any files with any extensions, even folders, when I press TAB.
What I want from them is:
For my specific situation, I put the certificate file inside the 2nd sub-directory of the current working directory.
I tried adding
Get-ChildItem -Recurse -ErrorAction 'SilentlyContinue'
to them but that didn't fix it.
By default, PowerShell falls back to its default tab-completion whenever a custom completer returns nothing (which is technically the [System.Management.Automation.Internal.AutomationNull]::Value
singleton), which makes it perform the usual completion of all file and subdirectory names in the current directory.
You can suppress this fallback behavior if you return $null
instead, so that if your custom completer found no matches, no completions are offered at all.
Caveat: As of of PowerShell 7.3.4, this seemingly only works with Register-ArgumentCompleter
calls, not with parameter-individual ArgumentCompleter
attributes using script blocks, and it isn't clear if this an officially supported mechanism or if there even is one.
However, as Santiago Squarzon has discovered, fallback can also be prevented if you use the ArgumentCompleter
attribute with a custom class that implements IArgumentCompleter
rather than with a script block.
return $null
as an opt-in to falling back to default completion.As an aside, as of PowerShell 7.3.4:
[int]
) - see GitHub issue #14147As Santiago points out, implementing unconstrained use of Get-ChildItem -Recurse
in a tab-completer is problematic, as it could result in a potentially prohibitively lengthy operation; however, you can use the -Depth
parameter to limit the recursion to a fixed depth.
-Depth 1
limits the lookups to the content of the input directory itself and that of its immediate subdirectories.Therefore, try something like the following:
$ArgumentCompleterCertPath = {
# Note the use of -Depth 1
# Enclosing the $results = ... assignment in (...) also passes the value through.
($results = Get-ChildItem -Depth 1 -Filter *.cer | foreach-object { "`"$_`"" })
if (-not $results) { # No results?
$null # Dummy response that prevents fallback to the default file-name completion.
}
}
Register-ArgumentCompleter -CommandName "Foo" -ParameterName "CertPath" -ScriptBlock $ArgumentCompleterCertPath