From b0ba045090b9abbd742859b62b37b43d765c00a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Fornal?= <24961583+Florian3k@users.noreply.github.com> Date: Sun, 17 Mar 2024 21:15:34 +0100 Subject: [PATCH] Repl - method signatures in autocomplete (#19917) Closes #17367 This PR brings method signatures to repl completions, as in Scala 2 repl (but with syntax highlighting). After typing `List(1).max`: ![image](https://github.com/scala/scala3/assets/24961583/0846e7b1-ff44-4e95-aaff-398069821b2f) --- .../src/dotty/tools/repl/ReplDriver.scala | 52 ++++++++++++------- compiler/test/dotty/tools/repl/ReplTest.scala | 2 +- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 5226ef0b4546..f8bba2f59fe1 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -31,6 +31,7 @@ import dotty.tools.dotc.util.{SourceFile, SourcePosition} import dotty.tools.dotc.{CompilationUnit, Driver} import dotty.tools.dotc.config.CompilerCommand import dotty.tools.io.* +import dotty.tools.repl.Rendering.showUser import dotty.tools.runner.ScalaClassLoader.* import org.jline.reader.* @@ -149,11 +150,36 @@ class ReplDriver(settings: Array[String], /** Blockingly read a line, getting back a parse result */ def readLine()(using state: State): ParseResult = { - val completer: Completer = { (_, line, candidates) => + given Context = state.context + val completer: Completer = { (lineReader, line, candidates) => + def makeCandidate(label: String) = { + new Candidate( + /* value = */ label, + /* displ = */ stripBackTicks(label), // displayed value + /* group = */ null, // can be used to group completions together + /* descr = */ null, // TODO use for documentation? + /* suffix = */ null, + /* key = */ null, + /* complete = */ false // if true adds space when completing + ) + } val comps = completions(line.cursor, line.line, state) - candidates.addAll(comps.asJava) + candidates.addAll(comps.map(_.label).distinct.map(makeCandidate).asJava) + val lineWord = line.word() + comps.filter(c => c.label == lineWord && c.symbols.nonEmpty) match + case Nil => + case exachMatches => + val terminal = lineReader.nn.getTerminal + lineReader.callWidget(LineReader.CLEAR) + terminal.writer.println() + exachMatches.foreach: exact => + exact.symbols.foreach: sym => + terminal.writer.println(SyntaxHighlighting.highlight(sym.showUser)) + lineReader.callWidget(LineReader.REDRAW_LINE) + lineReader.callWidget(LineReader.REDISPLAY) + terminal.flush() } - given Context = state.context + try { val line = terminal.readLine(completer) ParseResult(line) @@ -230,23 +256,10 @@ class ReplDriver(settings: Array[String], label /** Extract possible completions at the index of `cursor` in `expr` */ - protected final def completions(cursor: Int, expr: String, state0: State): List[Candidate] = - def makeCandidate(label: String) = { - - new Candidate( - /* value = */ label, - /* displ = */ stripBackTicks(label), // displayed value - /* group = */ null, // can be used to group completions together - /* descr = */ null, // TODO use for documentation? - /* suffix = */ null, - /* key = */ null, - /* complete = */ false // if true adds space when completing - ) - } - + protected final def completions(cursor: Int, expr: String, state0: State): List[Completion] = if expr.startsWith(":") then ParseResult.commands.collect { - case command if command._1.startsWith(expr) => makeCandidate(command._1) + case command if command._1.startsWith(expr) => Completion(command._1, "", List()) } else given state: State = newRun(state0) @@ -259,8 +272,7 @@ class ReplDriver(settings: Array[String], unit.tpdTree = tpdTree given Context = state.context.fresh.setCompilationUnit(unit) val srcPos = SourcePosition(file, Span(cursor)) - val completions = try Completion.completions(srcPos)._2 catch case NonFatal(_) => Nil - completions.map(_.label).distinct.map(makeCandidate) + try Completion.completions(srcPos)._2 catch case NonFatal(_) => Nil } .getOrElse(Nil) end completions diff --git a/compiler/test/dotty/tools/repl/ReplTest.scala b/compiler/test/dotty/tools/repl/ReplTest.scala index bc70f68e45fe..3e827a0f1e36 100644 --- a/compiler/test/dotty/tools/repl/ReplTest.scala +++ b/compiler/test/dotty/tools/repl/ReplTest.scala @@ -42,7 +42,7 @@ extends ReplDriver(options, new PrintStream(out, true, StandardCharsets.UTF_8.na /** Returns the `(, )`*/ def tabComplete(src: String)(implicit state: State): List[String] = - completions(src.length, src, state).map(_.value).sorted + completions(src.length, src, state).map(_.label).sorted.distinct extension [A](state: State) infix def andThen(op: State ?=> A): A = op(using state)