Daniel Sun created GROOVY-12403:
-----------------------------------

             Summary: Restore groovysh embedding extension points
                 Key: GROOVY-12403
                 URL: https://issues.apache.org/jira/browse/GROOVY-12403
             Project: Groovy
          Issue Type: Improvement
            Reporter: Daniel Sun


h3. Context

The Gremlin Console (Apache TinkerPop) embeds groovysh. In Groovy 4, a host 
could install an {{ImportCustomizer}} and 
{{ASTTransformationCustomizer(ThreadInterrupt)}} on Groovysh's 
{{CompilerConfiguration}}, and extend the command set. The Groovy 5 rewrite 
onto JLine 3 (GROOVY-8162) removed those seams.

Reported on the dev list: 
[https://lists.apache.org/thread/c66x4p4mhvmk7dojhddywzsgr4gpngs5]

h3. What's missing

# *Compiler configuration.* {{GroovyEngine}} builds the shell as {{new 
GroovyShell(classLoader, sharedData)}} with no configuration. 
{{CompilerConfiguration.DEFAULT}} is immutable, so there is no back door. 
Executing {{import}} statements works, but the engine records them as snippets 
and re-prepends them on every evaluation — expensive when the host preloads 
dozens of star / static-star imports.
# *Embedding hooks on Main.* The Map overload of {{Main.start}} only takes 
binding variables. Terminal, registries, prompt, banner, result printing, and 
error handling are hardcoded. Hosts copy roughly 230 lines of {{Main}} and 
re-diff them against every Groovy release.
# *ExtraConsoleCommands visibility.* {{/clear}}, {{/pwd}}, {{/cd}}, the 
POSIX-style commands, and {{/!}} live in a protected nested class. That 
compiles from another package in Groovy, but not from Java or under 
{{@CompileStatic}}.
# *Class loader access.* The engine class loader field is protected. A getter 
avoids a subclass whose only job is to read it (Grape + {{ServiceLoader}} 
plugin install).

h3. Direction

Keep the binding map as bindings only. Do *not* add magic keys — they collide 
with user variables and are awkward from Java.

* {{GroovyEngine(CompilerConfiguration)}} and a protected {{createShell}} 
override, so customizers apply at compile time instead of as snippets.
* {{GroovyshOptions}} plus {{Main.start(GroovyshOptions, ...)}} for extra 
{{CommandRegistry}} instances, a prompt supplier, result and error handlers, a 
banner toggle, and an optional {{Terminal}}.
* {{ExtraConsoleCommands}} as a public top-level class; keep 
{{Main.ExtraConsoleCommands}} as a deprecated subclass for existing Groovy 
callers.
* {{GroovyEngine.getClassLoader()}}.

Mark {{GroovyshOptions}} incubating so the shape can still settle after a first 
embedder uses it.

h3. Example

{code:groovy}
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
    new ImportCustomizer().addStarImports('java.util.concurrent.atomic'),
    new ASTTransformationCustomizer(ThreadInterrupt))

Main.start(GroovyshOptions.builder()
    .compilerConfiguration(config)
    .prompt { 'gremlin> ' }
    .showBanner(false)
    .build(), args)
{code}




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to