From f279894a3efe84b85e9becfd0ce96aaa0c21bfbd Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 2 Sep 2015 13:23:44 +1000 Subject: Use the presentation compiler to drive REPL tab completion The old implementation is still avaiable under a flag, but we'll remove it in due course. Design goal: - Push as much code in src/interactive as possible to enable reuse outside of the REPL - Don't entangle the REPL completion with JLine. The enclosed test case drives the REPL and autocompletion programatically. - Don't hard code UI choices, like how to render symbols or how to filter candidates. When completion is requested, we wrap the entered code into the same "interpreter wrapper" synthetic code as is done for regular execution. We then start a throwaway instance of the presentation compiler, which takes this as its one and only source file, and has a classpath formed from the REPL's classpath and the REPL's output directory (by default, this is in memory). We can then typecheck the tree, and find the position in the synthetic source corresponding to the cursor location. This is enough to use the new completion APIs in the presentation compiler to prepare a list of candidates. We go to extra lengths to allow completion of partially typed identifiers that appear to be keywords, e.g `global.def` should offer `definitions`. Two secret handshakes are included; move the the end of the line, type `// print` and you'll see the post-typer tree. `// typeAt 4 6` shows the type of the range position within the buffer. The enclosed unit test exercises most of the new functionality. --- .../nsc/interpreter/jline/JLineDelimiter.scala | 2 +- .../tools/nsc/interpreter/jline/JLineReader.scala | 40 ++++++++++++---------- 2 files changed, 23 insertions(+), 19 deletions(-) (limited to 'src/repl-jline/scala/tools') diff --git a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineDelimiter.scala b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineDelimiter.scala index c18a9809a0..89e849429d 100644 --- a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineDelimiter.scala +++ b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineDelimiter.scala @@ -11,7 +11,7 @@ import _root_.jline.console.completer.ArgumentCompleter.{ ArgumentDelimiter, Arg // implements a jline interface class JLineDelimiter extends ArgumentDelimiter { - def toJLine(args: List[String], cursor: Int) = args match { + def toJLine(args: List[String], cursor: Int): ArgumentList = args match { case Nil => new ArgumentList(new Array[String](0), 0, 0, cursor) case xs => new ArgumentList(xs.toArray, xs.size - 1, xs.last.length, cursor) } diff --git a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala index f0fce13fe8..5082c99a76 100644 --- a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala +++ b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala @@ -15,7 +15,7 @@ import jconsole.history.{History => JHistory} import scala.tools.nsc.interpreter -import scala.tools.nsc.interpreter.Completion +import scala.tools.nsc.interpreter.{Completion, JLineCompletion, NoCompletion} import scala.tools.nsc.interpreter.Completion.Candidates import scala.tools.nsc.interpreter.session.History @@ -121,23 +121,27 @@ private class JLineConsoleReader extends jconsole.ConsoleReader with interpreter def initCompletion(completion: Completion): Unit = { this setBellEnabled false - if (completion ne interpreter.NoCompletion) { - val jlineCompleter = new ArgumentCompleter(new JLineDelimiter, - new Completer { - val tc = completion.completer() - def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = { - val buf = if (_buf == null) "" else _buf - val Candidates(newCursor, newCandidates) = tc.complete(buf, cursor) - newCandidates foreach (candidates add _) - newCursor - } - } - ) - - jlineCompleter setStrict false - - this addCompleter jlineCompleter - this setAutoprintThreshold 400 // max completion candidates without warning + // adapt the JLine completion interface + def completer = + new Completer { + val tc = completion.completer() + def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = { + val buf = if (_buf == null) "" else _buf + val Candidates(newCursor, newCandidates) = tc.complete(buf, cursor) + newCandidates foreach (candidates add _) + newCursor + } + } + + // a last bit of nastiness: parsing help depending on the flavor of completer (fixme) + completion match { + case _: JLineCompletion => + val jlineCompleter = new ArgumentCompleter(new JLineDelimiter, completer) + jlineCompleter setStrict false + this addCompleter jlineCompleter + case NoCompletion => () + case _ => this addCompleter completer } + setAutoprintThreshold(400) // max completion candidates without warning } } -- cgit v1.2.3