I have a function whose name
parameter I want to provide tab completion for, the tab completion values are file names found in c:\temp
. On top of using the files in c:\temp
as values, I also want to add additional tab completion values.
Below is the function
Function foo{
Param(
[ValidateSet([layoutNames], ErrorMessage = """{0}"" Is not a valid Layout name")]
$Name
)
$name
}
and the layoutNames
class, which is used by the name
parameter:
Class layoutNames : System.Management.Automation.IValidateSetValuesGenerator{
[string[]] GetValidValues(){
#return @((Get-ChildItem -path 'c:\temp' -File).BaseName, "valueFoo", "valueBar") #tab completetion only suggests "valueFoo" and "valueBar"
#return @("valueFoo", "valueBar", (Get-ChildItem -path 'c:\temp' -File).BaseName) #tab completetion only suggests "valueFoo" and "valueBar"
return @( #tab completetion suggests "valueFoo" and "valueBar" and the file names.
"valueFoo", "valueBar"
(Get-ChildItem -path 'c:\temp' -File).BaseName
)
}}
With the above, only the third return
example works, the only difference being a new line....I think.
I spent quite some time trying to figure this out, I initially started with a return statement that looked like this:
return [string[]]("valueFoo", "valueBar", (Get-ChildItem -path 'c:\temp' -File).BaseName)
but kept changing it around as nothing was working, until I eventually worked by way to using the array operator @()
.
The crux of my issue is that why does the class, when declared in the following manner not work as intended with the foo
function, that is suggest both valueFoo
, valueBar
and the files names in c:\temp
Class layoutNames : System.Management.Automation.IValidateSetValuesGenerator{
[string[]] GetValidValues(){
#return [string[]]("valueFoo", "valueBar",(Get-ChildItem -path 'C:\Users\INDESK\AppData\Roaming\GPSoftware\Directory Opus\Layouts' -File).BaseName) # no files names are suggested. only 'valueFoo' and 'valueBar' are suggested
#return [string[]]("valueFoo", "valueBar",((Get-ChildItem -path 'C:\Users\INDESK\AppData\Roaming\GPSoftware\Directory Opus\Layouts' -File).BaseName)) # no files names are suggested. only 'valueFoo' and 'valueBar' are suggested
return [string[]](((Get-ChildItem -path 'C:\Users\INDESK\AppData\Roaming\GPSoftware\Directory Opus\Layouts' -File).BaseName),"valueFoo", "valueBar") # no files names are suggested. only 'valueFoo' and 'valueBar' are suggested
}}
Am on pwsh 7.4/win11
Reason why the third example works is because there is no comma before the Get-ChildItem
expression. See comma operator ,
details:
In expression mode, as a unary operator, the comma creates an array with just one member.
$arr = @( , (0..10))
$arr[0]
# 0
# 1
# 2
# etc
It's like doing:
$item = [object[]]::new(1)
$item[0] = 0..10
$arr = @($item)
$arr[0]
# 0
# 1
# 2
# etc
If you put all expressions in a single line then you're essentially risking creating a jagged array when Get-ChildItem
outputs 2 or more items:
$arr = @(
'valueFoo', 'valueBar', (0..2)
)
$arr[2]
# 0
# 1
# 2
And if it returns no items then you would end up with a null
element:
$arr = @(
'valueFoo', 'valueBar', (& { })
)
$arr.Count # 3 (Index 2 is `$null`)
If you're using the Array subexpression operator @( )
and want to put all elements in a single line, then the best you can do is to use ;
as your item separator (which symbolizes a new line / new statement):
$arr = @(
'valueFoo'; 'valueBar'; (0..2)
)
$arr.Count # 5 (the third expression was enumerated)
$arr = @(
'valueFoo'; 'valueBar'; (& { })
)
$arr.Count # 2 (`AutomationNull.Value` didn't count as array element)