New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Completion Candidates in Groovy Scripts? #1258
Comments
Hi, good question! @Command(name = "myscript", subcommands = GenerateCompletion.class)
@PicocliScript
@Option(names = "-x")
@Field int x
println "hello $x" Assuming you can execute this script with a wrapper shell script like source <(./myscript.sh generate-completion) |
@remkop Thank you very much for the help. What about a pure groovy script, i.e. no classes? i.e. when there's only the In a Groovy Script, something like the example in documentation: static class MyAbcCandidates extends ArrayList<String> {
MyAbcCandidates() { super(Arrays.asList("A", "B", "C")); }
} would not be possible. Is there any other alternative to specify the candidates (since the |
Oops, I misread your question! |
The problem seems to be that the class defined in the script cannot be referred to from the script itself. class MyAbcCandidates extends ArrayList<String> {
MyAbcCandidates() { super(Arrays.asList("A", "B", "C")); }
}
@Option(names = '-s', completionCandidates = MyAbcCandidates.class)
@Field String s The above script gives this error:
I am not sure yet if this can be avoided by referencing the class in a different way. A different approach, that may not meet your requirements but does successfully generate completion candidates, is using an import picocli.CommandLine.Model.CommandSpec
import picocli.CommandLine
import static picocli.CommandLine.*
@Command(name="testCompletionCandidates")
@picocli.groovy.PicocliScript2
import groovy.transform.Field
@picocli.CommandLine.Spec
@Field CommandSpec spec;
enum MyType { A, B, C}
@Option(names = '-x')
@Field MyType x
Iterable<String> iter = spec.findOption("-x").completionCandidates()
println iter This script successfully compiles and prints |
I found a nicer solution, but it requires some changes in picocli. The idea is to support closures in the picocli annotations. So, for example: import picocli.CommandLine.Model.CommandSpec
import picocli.CommandLine
import static picocli.CommandLine.*
@Command(name="testCompletionCandidatesWithClosure")
@picocli.groovy.PicocliScript2
import groovy.transform.Field
@picocli.CommandLine.Spec
@Field CommandSpec spec;
@Option(names = '-s', completionCandidates = {["A", "B", "C"]}) // <-- a closure that returns a List (which implements Iterable)
@Field String s
Iterable<String> iter = spec.findOption("-s").completionCandidates()
println iter That looks very natural, and seems like a nice feature to support. It does not seem that difficult to support. I did some prototyping and the below patch is sufficient to make the above example work. The below patch supports Groovy closures in all places where a I haven't committed it yet because I have some other work in progress, and I want to add some more tests for other use cases than just completion candidates: things like type converter, default provider, parameter consumer, and version provider. Also, all this needs to be documented. Do you feel like providing a pull request for this?
|
@remkop Wow, this looks fantastic ! The main usecase for "standalone" Groovy Scripts is as a replacement for fragile and hard to maintain Bash scripts. The Groovy Scripts can be distributed as simple script source file ( so that security and everybody is happy ), without any manual dependencies. ( so just by using Picocli is what makes them really usable :) . |
@aadrian Glad you like the idea! Are you interested in providing a pull request that takes the prototype above and adds some test cases and documentation? |
@remkop yes. I'll try to add some tests and update the documentation for this scenario. |
@aadrian Great! Thank you! |
One idea to improve on this further: we can pull the logic from The |
I investigated a bit further; pulling the logic into DefaultFactory looks good, results as expected. @Option(names = ['-a', '--algorithm'],
converter = [{ str -> MessageDigest.getInstance(str) }]) //closure is not passed to picocli from Groovy...
MessageDigest algorithm; I will raise this on the Groovy mailing list, to see if this is a Groovy issue. Please find the latest version of the patch below.
|
The below code demonstrates the issue when trying to pass a Groovy closure to the class ClosureTest {
static class Demo {
@picocli.CommandLine.Option(names = "-x",
completionCandidates = {["A", "B", "C"]},
converter = [{ str -> java.security.MessageDigest.getInstance(str) }])
java.security.MessageDigest digest
}
static void main(String[] args) {
def annotation = Demo.class.getDeclaredField("digest").getAnnotation(picocli.CommandLine.Option)
Class ok = annotation.completionCandidates()
assert ok != null
assert Closure.class.isAssignableFrom(ok)
assert ["A", "B", "C"] == ((Closure) ok.getConstructor(Object, Object).newInstance(null, null)).call()
Class[] bad = annotation.converter()
assert bad != null
assert bad.length == 1 // this assert fails:
//Exception in thread "main" Assertion failed:
//
//assert bad.length == 1
// | | |
// [] 0 false
//
// at org.codehaus.groovy.runtime.InvokerHelper.assertFailed(InvokerHelper.java:434)
// at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.assertFailed(ScriptBytecodeAdapter.java:670)
// at closure.ClosureTest.main(ClosureTest.groovy:18)
}
} I will pass this example on to the Groovy community. |
Support for the array values in the annotations will require a change in Groovy. |
My initial inclination is to add support in master and the GROOVY_3_0_X branch. Does this work for you? |
@paulk-asert First, thanks for the amazingly quick turnaround! I can document in the picocli manual that Groovy 3.0.7 or higher is required to be able to use closures for type converters in the annotations. So master and GROOVY_3_0_X branch is fine. I think it is a nice feature, and I would not mind having this in Groovy 2.5.14+ as well, but it is not problem for me. |
@remkop merged. Thanks for the suggested enhancement. I suspect others will also like this capability. For now, I will stick with 3.0.7+ just to give IDE folks (IDEA/Netbeans/Eclipse) a more well-defined boundary for adding support. But if there is sufficient interest over time, we can merge/back-port to the 2_5_X branch. |
Makes sense. Thank you again! |
Pushed a fix to master that implements support for closures in the new default factory, tests and documentation. |
…ngle Parameter Type Converters section
… from Single Parameter Type Converters section" This reverts commit ccd97b0.
… from Single Parameter Type Converters section" This reverts commit ccd97b0.
Hi,
is it possible to have completion candidates https://picocli.info/#_completion_candidates_variable in Groovy Scripts ?
( I could not find any working example :( )
Thanks in advance.
The text was updated successfully, but these errors were encountered: