summaryrefslogtreecommitdiff
path: root/src/repl/scala/tools/nsc/interpreter/ILoop.scala
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2015-06-17 11:06:31 -0700
committerAdriaan Moors <adriaan.moors@typesafe.com>2015-06-18 10:33:33 -0700
commitdfb70b632c1e8a2c6ce27eaacb74dbbb47ce9532 (patch)
tree277124732e329eb78b6168ad8ad3a6c908fb407f /src/repl/scala/tools/nsc/interpreter/ILoop.scala
parent43139faa4f4348b95907e06883f2fefb41ea3a3b (diff)
downloadscala-dfb70b632c1e8a2c6ce27eaacb74dbbb47ce9532.tar.gz
scala-dfb70b632c1e8a2c6ce27eaacb74dbbb47ce9532.tar.bz2
scala-dfb70b632c1e8a2c6ce27eaacb74dbbb47ce9532.zip
SI-9339 Support classpaths with no single compatible jline
As usual, the repl will use whatever jline 2 jar on the classpath, if there is one. Failing that, there's a fallback and an override. If instantiating the standard `jline.InteractiveReader` fails, we fall back to an embedded, shaded, version of jline, provided by `jline_embedded.InteractiveReader`. (Assume `import scala.tools.nsc.interpreter._` for this message.) The instantiation of `InteractiveReader` eagerly exercises jline, so that a linkage error will result if jline is missing or if the provided one is not binary compatible. The property `scala.repl.reader` overrides this behavior, if set to the FQN of a class that looks like `YourInteractiveReader` below. ``` class YourInteractiveReader(completer: () => Completion) extends InteractiveReader ``` The repl logs which classes it tried to instantiate under `-Ydebug`. # Changes to source & build The core of the repl (`src/repl`) no longer depends on jline. The jline interface is now in `src/repl-jline`. The embedded jline + our interface to it are generated by the `quick.repl` target. The build now also enforces that only `src/repl-jline` depends on jline. The sources in `src/repl` are now sure to be independent of it, though they do use reflection to instantiate a suitable subclass of `InteractiveReader`, as explained above. The `quick.repl` target builds the sources in `src/repl` and `src/repl-jline`, producing a jar for the `repl-jline` classes, which is then transformed using jarjar to obtain a shaded copy of the `scala.tools.nsc.interpreter.jline` package. Jarjar is used to combine the `jline` jar and the `repl-jline` into a new jar, rewriting package names as follows: - `org.fusesource` -> `scala.tools.fusesource_embedded` - `jline` -> `scala.tools.jline_embedded` - `scala.tools.nsc.interpreter.jline` -> `scala.tools.nsc.interpreter.jline_embedded` Classes not reachable from `scala.tools.**` are pruned, as well as empty dirs. The classes in the `repl-jline` jar as well as those in the rewritten one are copied to the repl's output directory. PS: The sbt build is not updated, sorry. PPS: A more recent fork of jarjar: https://github.com/shevek/jarjar.
Diffstat (limited to 'src/repl/scala/tools/nsc/interpreter/ILoop.scala')
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ILoop.scala40
1 files changed, 30 insertions, 10 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
index 3ce9668b97..a3047ccc8e 100644
--- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
@@ -26,6 +26,8 @@ import scala.concurrent.{ ExecutionContext, Await, Future, future }
import ExecutionContext.Implicits._
import java.io.{ BufferedReader, FileReader }
+import scala.util.{Try, Success, Failure}
+
/** The Scala interactive shell. It provides a read-eval-print loop
* around the Interpreter class.
* After instantiation, clients should call the main() method.
@@ -860,18 +862,36 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
* with SimpleReader.
*/
def chooseReader(settings: Settings): InteractiveReader = {
- def mkJLineReader(completer: () => Completion): InteractiveReader =
- try new jline.JLineReader(completer)
- catch {
- case ex@(_: Exception | _: NoClassDefFoundError) =>
- Console.println(f"Failed to created JLineReader: ${ex}%nFalling back to SimpleReader.")
- SimpleReader()
- }
-
if (settings.Xnojline || Properties.isEmacsShell) SimpleReader()
else {
- if (settings.noCompletion) mkJLineReader(() => NoCompletion)
- else mkJLineReader(() => new JLineCompletion(intp))
+ type Completer = () => Completion
+ type ReaderMaker = Completer => InteractiveReader
+
+ def instantiate(className: String): ReaderMaker = completer => {
+ if (settings.debug) Console.println(s"Trying to instantiate a InteractiveReader from $className")
+ Class.forName(className).getConstructor(classOf[Completer]).
+ newInstance(completer).
+ asInstanceOf[InteractiveReader]
+ }
+
+ def mkReader(maker: ReaderMaker) =
+ if (settings.noCompletion) maker(() => NoCompletion)
+ else maker(() => new JLineCompletion(intp)) // JLineCompletion is a misnomer -- it's not tied to jline
+
+ def internalClass(kind: String) = s"scala.tools.nsc.interpreter.$kind.InteractiveReader"
+ val readerClasses = sys.props.get("scala.repl.reader").toStream ++ Stream(internalClass("jline"), internalClass("jline_embedded"))
+ val readers = readerClasses map (cls => Try { mkReader(instantiate(cls)) })
+
+ val reader = (readers collect { case Success(reader) => reader } headOption) getOrElse SimpleReader()
+
+ if (settings.debug) {
+ val readerDiags = (readerClasses, readers).zipped map {
+ case (cls, Failure(e)) => s" - $cls --> " + e.getStackTrace.mkString(e.toString+"\n\t", "\n\t","\n")
+ case (cls, Success(_)) => s" - $cls OK"
+ }
+ Console.println(s"All InteractiveReaders tried: ${readerDiags.mkString("\n","\n","\n")}")
+ }
+ reader
}
}