
This fails (EXPECTED TO WORK)
scriptblock variable gets passed to the attribute and fails for some reason

But this works
however when there is no variable but pure scriptblock - it works

Do i do something wrong?
I think what the error message is saying is that attributes in general only accept _literals_ as arguments - either as constants or as script-block _literals_.
In other words: a script block itself cannot be specified via a _variable_, as you've attempted.
Well, it is ok, but i think it would be good to be able to pass it as a variable.
If you want to use a variable to store the completer script block (e.g., in order to associate it with _multiple_ functions) you can use the Register-ArgumentCompleter _cmdlet_ instead of the ArgumentCompleter attribute.
You can also delegate to a function, like
function completeme{
param($c, $p, $w, $a, $fb)
...
}
function Verb-Noun{
param(
[ArgumentCompleter({completeme @args})
[string] $First
)
}
@powercode: That's a great alternative, but I suggest only using it in the context of a _module_, so as to ensure that the delegate (helper function) is packaged with the function(s) that rely on it.
Well, function is a great option, but in case there is some 'dynamicity' is needed it is easier to generate a scriptblock, than a function
@eosfor: I see three basic scenarios:
(a) "Bake" a script-block literal directly in the function, with no external dependencies (other than relying on standard features and cmdlets to generate completions)
(b) In the context of a module, use a helper function that your reference from the script block the way @powercode suggests, so as to either use the same completer with multiple functions or to separate the implementation of a lengthy completer from the function's main code.
(c) Associate arbitrary completers with existing functions, possibly by someone other than the function author, using Register-ArgumentCompleter - though this technique could also be used module-internally. Completers are indeed passed as _script blocks_ to Register-ArgumentCompleter.
Does your scenario not fall into one of the 3 categories above?
@mklement0:
Yes, I think (a) is something I talking about. However if a script-block is big and complex, and we have a lot of parameters to "auto-complete", then a function definition will be really long and unreadable.
Using variables will give an option to move script-block code off a function definition. But avoiding complexity of generating a full-featured function, especially if a function is not needed for anything else but for completer.
IMHO of course.
@eosfor:
then a function definition will be really long and unreadable.
So in that case you can reference a unit of code the way @powercode suggests, whether that unit of code is
a function ([ArgumentCompleter({ completeme @args })])
or a script block ([ArgumentCompleter({ & $sb2 @args})])
avoiding complexity of generating a full-featured function
The bodies of functions are script blocks, so the only complexity that a function definition adds is the word function:
$sb2 = { ... }
# vs.
function sb2 { ... }
Aha, this is what i was looking for :). It still isn't obvious :D, IMHO. Kind of script-block calling another script-block, looks like a hack
$sb = {
Get-Process | select -ExpandProperty name
}
function Verb-Noun {
[CmdletBinding()]
param (
[ArgumentCompleter({ & $sb })]
$name
)
process {
}
}
Especially (i'm not a dev however, so may misinterpret things), here there is a piece of code which takes a script-block directly (Line 45):
https://github.com/PowerShell/PowerShell/blob/c1c5344a8897262433141ecbc13bb06ac2c4bbef/src/System.Management.Automation/engine/CommandCompletion/ExtensibleCompletion.cs#L21-L54
so my expectation were that it should take something of type ScriptBlock, like variable. But looks like this error is generated elsewhere.
But, like i said, this { & $sb } approach works for me.
@eosfor:
Note that in order for your completer to work fully as expected you must:
pass arguments through to it, as in @powercode's example: [ArgumentCompleter({ & $sb @Args })]
declare parameters in your script block so you can _access the part (prefix) of the argument to complete that the user has already typed_.
Here's the full example:
$sb = {
param($cmdName, $paramName, $wordToComplete)
Get-Process $wordToComplete* | select-Object -ExpandProperty name
}
function Verb-Noun {
[CmdletBinding()]
param (
[ArgumentCompleter({ & $sb @Args })]
$name
)
process {
}
}
Again, note that there's no advantage to using a script block over defining a function here.
As for the rationale behind only allowing literals (and constants $null, $True, and $False) in attribute arguments:
Others are better qualified to comment on that, but my guess is that the main reason is that function definitions are parsed _before_ execution begins, which means that at that time the value of variable $sb is not known yet.
By contrast, a script-block _literal_ can be parsed, and any variables it references aren't resolved until the block is later _invoked_ at runtime.
Kind of script-block calling another script-block, looks like a hack
Again, using a _function_ is the better choice.
@eosfor The restriction for static arguments in attibutes is a general check done at parse time. See SemanticChecks.cs
I am not really wanting to necro this thread but as someone who was fooling around with this recently, afaict if you use a function in an argument completer attribute then that function has to be exported with the module. I did not see if a script block had the same limitation so do not know if that's an advantage/disadvantage or not. If I figure that out I'll update this comment.
Update: seems maybe this is reported here https://github.com/PowerShell/PowerShell/issues/7265
Most helpful comment
You can also delegate to a function, like