If I type onto the command line:
[pscustomobject]@{ name = 'test'; description='doing test' } | Select-Object
I can press tab and complete the property names—besides the default properties, either of the defined properties name
and description
. Select-Object
is aware of the name
and description
properties on the input object and will autocomplete them with standard tab behavior.
Now if I use a custom function, such as:
function pipeobject {
param(
[Parameter(ValueFromPipeline)]
$InputObject,
[Parameter(Position=0)]
$Property
)
process {
$Property | Foreach-Object { $InputObject.$_ }
}
}
and enter:
[pscustomobject]@{ name = 'test'; description='doing test' } | pipeobject
I cannot tab complete the -Property
parameter for any of the properties on the input object.
Note: I am aware of this reply which recommends using output types. However, my inquiry refers to parsing the properties of a generic pscustomobject, which Select-Object
seems capable of.
How can I reproduce Select-Object
's tab completion behavior on the pipeline for properties in my custom functions?
What you're describing, inferring members from a pscustomobject
from pipeline, is one of many great tab completion improvements in PowerShell v7.4 from MartinGC94 (PR #18682
in this case) and short answer is you won't find an easy way to replicate what he did in a robust way but to show you how you can approach the problem by looking at the CommandAst
should be a start:
Register-ArgumentCompleter -CommandName pipeobject -ParameterName Property -ScriptBlock {
param(
[string] $CommandName,
[string] $ParameterName,
[string] $WordToComplete,
[System.Management.Automation.Language.CommandAst] $CommandAst,
[System.Collections.IDictionary] $FakeBoundParameters
)
$ast = $CommandAst.Parent.FindAll({
$args[0] -is [System.Management.Automation.Language.ConvertExpressionAst] },
$true)
if (-not $ast.Child.KeyValuePairs) {
return
}
$hash = [System.Collections.Generic.HashSet[string]]::new(
[System.StringComparer]::InvariantCultureIgnoreCase)
foreach ($item in $ast.Child.KeyValuePairs.Item1) {
$key = $item.ToString()
if (-not $hash.Add($key)) {
continue
}
if ($key.StartsWith($WordToComplete, [StringComparison]::InvariantCultureIgnoreCase)) {
$item
}
}
}