Skip to content

Conversation

@gbrail
Copy link
Collaborator

@gbrail gbrail commented Dec 15, 2025

The "tools" module has a bunch of code that tries to use reflection to load a nice console using JLine when it's in the classpath.

Now that our codebase is modular, do this in a nicer way by creating a new top-level "rhino-cli" module for this. Include this module in "rhino-all" so that the default experience has a nice CLI.

Update the various "tools" programs to load the JLine console if it is available, and if it is not, or not on an interactive terminal, fall back to the old-fashioned console based on System.in and System.out.

This does add JLine as a dependency in "rhino-all." If this is an issue for users, we could "shade" it using a plugin, or we could just suggest that folks who don't want a full-fledged CLI use the new modules instead of "rhino-all" and not include the new CLI module if they don't want it.

This doesn't carry forward the old completer code that we used to have -- I didn't find that to work reliably with the current JLine. There's room for future improvement there.

@gbrail gbrail marked this pull request as ready for review December 15, 2025 06:31
@gbrail
Copy link
Collaborator Author

gbrail commented Dec 15, 2025

This might be helpful to some of you, it certainly improves the experience for me, I'd love it if a few people could try it out.

@aardvark179
Copy link
Contributor

This looks pretty good—though we will need to ensure we don't pull in JLine on our fork. You might want to tweak the default run cradle task a little because it run the REPL without JLine.

@Override
public boolean isSupported() {
// Only allow JLine to be used if we have full functionality.
return (terminal != null
Copy link
Contributor

@ZZZank ZZZank Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if old behaviour is good behaviour, but I think it is possible to avoid changing it, by doing something like this:

private final Optional<Terminal> terminal; // type in generic param so that at runtime JVM will load `Optional.class` instead of `Terminal.class`

public JLineConsoleProvider() {
    try {
        terminal = Optiona.of(...);
    } catch (Throwable t) {
        // capture `Throwable`, including `NoClassDefFoundError` when JLine is not present
        terminal = Optional.empty();
    }
}

And by doing so we can merge rhino-cli into rhino-tools because JLine can be compileOnly dependency

(please ignore the fact that I targeted this comment at line 34 for no reason)

@p-bakker
Copy link
Collaborator

Great, tried getting this going in the past, but was never able to

Think adding some info about this to the docs would be useful

@gbrail
Copy link
Collaborator Author

gbrail commented Dec 18, 2025

Thanks for looking -- I think I might want to take another approach, and maybe create an issue first.

In general:

  • Move the whole "CLI" out of "tools" into a separate module, so that people using Rhino can have a "CLI" without the non-standard built-in functionality if they want.
  • Make that "CLI" module optionally dependent on JLine the way that @ZZZank suggested
  • Package the whole thing together in rhino-all so that it still works like it used to work, just with JLine.

More soon, thanks.

Copy link
Contributor

@andreabergia andreabergia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very nice!

Though I wonder if we actually need the rhino-cli module and can't just add the dependency via compileOnly in rhino-tool, to reduce the proliferation of modules.

@aardvark179 we already manage the publication of the "all-in-one" jar in a custom way in our fork, handling this as well won't be a problem.

@gbrail
Copy link
Collaborator Author

gbrail commented Dec 19, 2025

Check out this latest revision -- it works the way that you suggest, and is much simpler.

Copy link
Contributor

@ZZZank ZZZank left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit confused by the fact that private final Terminal terminal; and Terminal t; will not cause an error in a JLine-free environment, but I did some basic tests and it works well

Replace the old code that tried to use reflection to load a JLine
console with a new "rhino-cli" module that includes JLine. If this
module is present and we are on an interactive terminal, we will use a
JLine console which supports command history and editing.
Include this module in rhino-all so that the default Rhino CLI uses it.
Move the JLine integration back into the "rhino-tools" module, but make
it optional using "compileOnly". The module will use JLine if it's in
the classpath and will fall back gracefully if it's not.

Include JLine in the "rhino-all" module so that the "rhino-all" JAR
contains JLine by default so that we have a nice initial user
experience.

This will give us command-line editing and history. Command completion
and highlighting are a task for another day.
* Move "run" tasks to rhino-tools from rhino-all
* Document various ways to run the shell

# Conflicts:
#	rhino-all/build.gradle
@gbrail gbrail merged commit fe31355 into mozilla:master Jan 10, 2026
12 checks passed
@gbrail gbrail deleted the greg-new-console branch January 10, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants