Skip to content

Commit b25af1e

Browse files
committed
Bring in a JLine-based console in a modular way
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.
1 parent 1c91d49 commit b25af1e

File tree

20 files changed

+420
-555
lines changed

20 files changed

+420
-555
lines changed

rhino-all/build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@ dependencies {
1616
implementation project(':rhino')
1717
implementation project(':rhino-tools')
1818
implementation project(':rhino-xml')
19+
implementation project(':rhino-cli')
1920
}
2021

2122
shadowJar {
2223
// Ensure that the "jar" from this step is the shadowed one that we want to
2324
// publish in Maven.
2425
archiveClassifier.set('')
26+
// Merge service files, but fail on other duplicates.
27+
duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN.
28+
mergeServiceFiles()
29+
filesNotMatching('META-INF/services/**') {
30+
duplicatesStrategy = DuplicatesStrategy.FAIL // Or FAIL.
31+
}
2532
}
2633

2734
startScripts {

rhino-cli/build.gradle

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
plugins {
2+
id 'rhino.library-conventions'
3+
}
4+
5+
dependencies {
6+
implementation project(':rhino')
7+
implementation project(':rhino-tools')
8+
implementation 'org.jline:jline-terminal:3.30.0'
9+
implementation 'org.jline:jline-reader:3.30.0'
10+
runtimeOnly 'org.jline:jline-terminal-ffm:3.30.0'
11+
runtimeOnly 'org.jline:jline-terminal-jni:3.30.0'
12+
}
13+
14+
publishing {
15+
publications {
16+
rhinotools(MavenPublication) {
17+
from components.java
18+
artifacts = [jar, sourceJar, javadocJar]
19+
pom.withXml {
20+
def root = asNode()
21+
22+
root.appendNode('name', 'rhino-cli')
23+
root.appendNode('description', "Rhino CLI support for JLine")
24+
root.appendNode("url", "https://mozilla.github.io/rhino/")
25+
26+
def p = root.appendNode("parent")
27+
p.appendNode("groupId", "org.sonatype.oss")
28+
p.appendNode("artifactId", "oss-parent")
29+
p.appendNode("version", "7")
30+
31+
def l = root.appendNode("licenses").appendNode("license")
32+
l.appendNode("name", "Mozilla Public License, Version 2.0")
33+
l.appendNode("url", "http://www.mozilla.org/MPL/2.0/index.txt")
34+
35+
def scm = root.appendNode("scm")
36+
scm.appendNode("connection", "scm:git:[email protected]:mozilla/rhino.git")
37+
scm.appendNode("developerConnection", "scm:git:[email protected]:mozilla/rhino.git")
38+
scm.appendNode("url", "[email protected]:mozilla/rhino.git")
39+
40+
def o = root.appendNode("organization")
41+
o.appendNode("name", "The Mozilla Foundation")
42+
o.appendNode("url", "http://www.mozilla.org")
43+
44+
def d = root.appendNode("developers")
45+
def dd = d.appendNode("developer")
46+
dd.appendNode("name", "Greg Brail")
47+
dd.appendNode("email", "[email protected]")
48+
}
49+
}
50+
}
51+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module org.mozilla.rhino.cli {
2+
requires org.mozilla.rhino;
3+
requires org.mozilla.rhino.tools;
4+
requires org.jline.terminal;
5+
requires org.jline.reader;
6+
7+
exports org.mozilla.javascript.cli;
8+
9+
provides org.mozilla.javascript.tools.ConsoleProvider with
10+
org.mozilla.javascript.cli.JLineConsoleProvider;
11+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package org.mozilla.javascript.cli;
2+
3+
import java.io.PrintStream;
4+
import org.jline.reader.EndOfFileException;
5+
import org.jline.reader.LineReader;
6+
import org.jline.reader.LineReaderBuilder;
7+
import org.jline.reader.UserInterruptException;
8+
import org.jline.terminal.Terminal;
9+
import org.mozilla.javascript.Scriptable;
10+
import org.mozilla.javascript.tools.Console;
11+
12+
public class JLineConsole implements Console {
13+
private final Terminal terminal;
14+
private final LineReader reader;
15+
16+
JLineConsole(Terminal t, Scriptable scope) {
17+
this.terminal = t;
18+
this.reader = LineReaderBuilder.builder().terminal(t).build();
19+
}
20+
21+
@Override
22+
public String getImplementation() {
23+
return "JLine " + terminal.getType();
24+
}
25+
26+
@Override
27+
public String readLine(String prompt) {
28+
try {
29+
return reader.readLine(prompt);
30+
} catch (UserInterruptException | EndOfFileException e) {
31+
// This will cause the "main" to cleanly exit
32+
return null;
33+
}
34+
}
35+
36+
@Override
37+
public String readLine() {
38+
try {
39+
return reader.readLine();
40+
} catch (UserInterruptException | EndOfFileException e) {
41+
return null;
42+
}
43+
}
44+
45+
@Override
46+
public void print(String msg) {
47+
terminal.writer().print(msg);
48+
}
49+
50+
@Override
51+
public void println(String msg) {
52+
terminal.writer().println(msg);
53+
}
54+
55+
@Override
56+
public void println() {
57+
terminal.writer().println();
58+
}
59+
60+
@Override
61+
public void flush() {
62+
terminal.writer().flush();
63+
}
64+
65+
@Override
66+
public PrintStream getOut() {
67+
return new PrintStream(terminal.output());
68+
}
69+
70+
@Override
71+
public PrintStream getErr() {
72+
return new PrintStream(terminal.output());
73+
}
74+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.mozilla.javascript.cli;
2+
3+
import java.io.IOException;
4+
import org.jline.terminal.Terminal;
5+
import org.jline.terminal.TerminalBuilder;
6+
import org.mozilla.javascript.Scriptable;
7+
import org.mozilla.javascript.tools.Console;
8+
import org.mozilla.javascript.tools.ConsoleProvider;
9+
10+
public class JLineConsoleProvider implements ConsoleProvider {
11+
private final Terminal terminal;
12+
13+
public JLineConsoleProvider() {
14+
Terminal t;
15+
try {
16+
// Initialize JLine so it will try to use a fancy console, but
17+
// won't print out a warning if it can't.
18+
t = TerminalBuilder.builder().system(true).dumb(true).build();
19+
} catch (IOException ioe) {
20+
t = null;
21+
}
22+
terminal = t;
23+
}
24+
25+
@Override
26+
public Console newConsole(Scriptable scope) {
27+
assert terminal != null;
28+
return new JLineConsole(terminal, scope);
29+
}
30+
31+
@Override
32+
public boolean isSupported() {
33+
// Only allow JLine to be used if we have full functionality.
34+
return (terminal != null
35+
&& !Terminal.TYPE_DUMB.equals(terminal.getType())
36+
&& !Terminal.TYPE_DUMB_COLOR.equals(terminal.getType()));
37+
}
38+
39+
@Override
40+
public boolean supportsEditing() {
41+
return true;
42+
}
43+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
org.mozilla.javascript.cli.JLineConsoleProvider

rhino-tools/src/main/java/module-info.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,9 @@
66
exports org.mozilla.javascript.tools.debugger;
77
exports org.mozilla.javascript.tools.jsc;
88
exports org.mozilla.javascript.tools.shell;
9+
10+
uses org.mozilla.javascript.tools.ConsoleProvider;
11+
12+
provides org.mozilla.javascript.tools.ConsoleProvider with
13+
org.mozilla.javascript.tools.shell.BasicConsoleProvider;
914
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.mozilla.javascript.tools;
2+
3+
import java.io.IOException;
4+
import java.io.PrintStream;
5+
6+
/**
7+
* A generic interface for a console that supports input from the user and displays input on the
8+
* screen.
9+
*/
10+
public interface Console {
11+
String getImplementation();
12+
13+
String readLine(String prompt) throws IOException;
14+
15+
String readLine() throws IOException;
16+
17+
void print(String msg);
18+
19+
void println(String msg);
20+
21+
void println();
22+
23+
void flush();
24+
25+
PrintStream getOut();
26+
27+
PrintStream getErr();
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.mozilla.javascript.tools;
2+
3+
import org.mozilla.javascript.Scriptable;
4+
5+
/**
6+
* This is a service interface to provide consoles. It allows there to be multiple console
7+
* implementations at runtime based on classpath.
8+
*/
9+
public interface ConsoleProvider {
10+
/** Create the console. Must not be called unless "isSupported" returned true. */
11+
Console newConsole(Scriptable scope);
12+
13+
/** Return whether the console is possible to use at all */
14+
boolean isSupported();
15+
16+
/** Return whether this console supports interactive command-line editing. */
17+
boolean supportsEditing();
18+
}

rhino-tools/src/main/java/org/mozilla/javascript/tools/ToolErrorReporter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
* <p>Currently used by both the shell and the compiler.
2626
*/
2727
public class ToolErrorReporter implements ErrorReporter {
28-
2928
public ToolErrorReporter(boolean reportWarnings) {
30-
this(reportWarnings, System.err);
29+
this.reportWarnings = reportWarnings;
30+
this.err = System.err;
3131
}
3232

3333
public ToolErrorReporter(boolean reportWarnings, PrintStream err) {
@@ -190,5 +190,5 @@ private String buildIndicator(int offset) {
190190
private static final String messagePrefix = "js: ";
191191
private boolean hasReportedErrorFlag;
192192
private boolean reportWarnings;
193-
private PrintStream err;
193+
private final PrintStream err;
194194
}

0 commit comments

Comments
 (0)