test.psm1:
$script:ProviderItem = [System.Management.Automation.CompletionResultType]::ProviderItem
function Get-Files {Get-ChildItem -Path 'C:\Windows\System32\WindowsPowerShell\v1.0\en-US\about_Functions*.txt'}
function Test
{
[CmdletBinding()]
param (
[Parameter(Mandatory, ParameterSetName = 'Name', Position = 0, ValueFromPipeline)]
[ArgumentCompleter({
param ($Command, $Parameter, $WordToComplete, $CommandAst, $FakeBoundParams)
Get-Files | Where-Object {$_.Name -like "*$WordToComplete*"} | ForEach-Object {
$resultName = $_.Name
$resultFN = $_.FullName
$toolTip = "File: $resultFN"
[System.Management.Automation.CompletionResult]::new($resultName, $resultFN, $script:ProviderItem, $toolTip)
} #ForEach-Object
})]
[System.String[]]$Name
)
begin {Write-Output $script:ProviderItem}
process { foreach ($n in $Name) {Write-Output $n} }
}
NOTES:
- This is for illustration; You could easily use
'ProviderItem' instead of a constant in the [System.Management.Automation.CompletionResult] constructor.
- In this example the
Get-Files function is intended to be a private (non-exported) function.
- I'm wondering why autocompletion works when
$ProviderItem is scoped as $global:ProviderItem but not $script:ProviderItem
- In the module manifest, even if
$ProviderItem is scoped globally I still have to export all functions, rather than just Test, in order to get tab-completion to work properly.
- Doesn't Work:
FunctionsToExport = 'Test'
- Tab completion falls back on
TabExpansion2 and lists child items in the current directory.
- Works:
FunctionsToExport = '*'
- Performs tab-completion as I expect.
- I thought this might have to do with scoping and PSReadLine, but ISE behaves the same way, so I'm obviously missing something critical.
Questions:
- How can I use
Get-Files inside an ArgumentCompleter block of a different function's parameter block(s), export only Test and still retain tab-completion?
- Can I avoid using the global scope for module-wide constants that are used in
ArgumentCompleter function param blocks?
Because the
ArgumentCompleterscriptblock has no knowledge about the Module it is being invoked in, thus has no knowledge about variables defined in the module scope. A simple way to prove this is the case is by changing theCompletionResultarguments to:Moreover, defining the variable as
$script:is not needed, all variables defined in the.psm1are already scoped to the commands in your module.Exactly the same applies for
Get-FilesifFunctionsToExport = 'Test', then the it is scoped to your module and the completer scriptblock has no knowledge about it, you would've to:A workaround can be to use a class that implements
IArgumentCompleterattribute, classes defined in the module scope can see the scoped variables and functions without issues, same applies toRegister-ArgumentCompleter.Sharing the class implementation here: