@@ -31,6 +31,7 @@ import dotty.tools.dotc.util.{SourceFile, SourcePosition}
3131import dotty .tools .dotc .{CompilationUnit , Driver }
3232import dotty .tools .dotc .config .CompilerCommand
3333import dotty .tools .io .*
34+ import dotty .tools .repl .Rendering .showUser
3435import dotty .tools .runner .ScalaClassLoader .*
3536import org .jline .reader .*
3637
@@ -149,11 +150,38 @@ class ReplDriver(settings: Array[String],
149150
150151 /** Blockingly read a line, getting back a parse result */
151152 def readLine ()(using state : State ): ParseResult = {
152- val completer : Completer = { (_, line, candidates) =>
153- val comps = completions(line.cursor, line.line, state)
154- candidates.addAll(comps.asJava)
155- }
156153 given Context = state.context
154+ val completer : Completer = { (lineReader, line, candidates) =>
155+ def makeCandidate (label : String ) = {
156+ new Candidate (
157+ /* value = */ label,
158+ /* displ = */ stripBackTicks(label), // displayed value
159+ /* group = */ null , // can be used to group completions together
160+ /* descr = */ null , // TODO use for documentation?
161+ /* suffix = */ null ,
162+ /* key = */ null ,
163+ /* complete = */ false // if true adds space when completing
164+ )
165+ }
166+ completions(line.cursor, line.line, state) match
167+ case Left (cmds) => candidates.addAll(cmds.map(makeCandidate).asJava)
168+ case Right (comps) =>
169+ val lineWord = line.word()
170+ candidates.addAll(comps.map(c => makeCandidate(c.label)).asJava)
171+ comps.filter(_.label == lineWord) match
172+ case Nil =>
173+ case exachMatches =>
174+ val terminal = lineReader.getTerminal
175+ lineReader.callWidget(LineReader .CLEAR )
176+ terminal.writer.println()
177+ exachMatches.foreach: exact =>
178+ exact.symbols.foreach: sym =>
179+ terminal.writer.println(SyntaxHighlighting .highlight(sym.showUser))
180+ lineReader.callWidget(LineReader .REDRAW_LINE )
181+ lineReader.callWidget(LineReader .REDISPLAY )
182+ terminal.flush()
183+ }
184+
157185 try {
158186 val line = terminal.readLine(completer)
159187 ParseResult (line)
@@ -230,39 +258,26 @@ class ReplDriver(settings: Array[String],
230258 label
231259
232260 /** Extract possible completions at the index of `cursor` in `expr` */
233- protected final def completions (cursor : Int , expr : String , state0 : State ): List [Candidate ] =
234- def makeCandidate (label : String ) = {
235-
236- new Candidate (
237- /* value = */ label,
238- /* displ = */ stripBackTicks(label), // displayed value
239- /* group = */ null , // can be used to group completions together
240- /* descr = */ null , // TODO use for documentation?
241- /* suffix = */ null ,
242- /* key = */ null ,
243- /* complete = */ false // if true adds space when completing
244- )
245- }
246-
261+ protected final def completions (cursor : Int , expr : String , state0 : State ): Either [List [String ], List [Completion ]] =
247262 if expr.startsWith(" :" ) then
248- ParseResult .commands.collect {
249- case command if command._1.startsWith(expr) => makeCandidate( command._1)
250- }
263+ Left ( ParseResult .commands.collect {
264+ case command if command._1.startsWith(expr) => command._1
265+ })
251266 else
252267 given state : State = newRun(state0)
253- compiler
254- .typeCheck(expr, errorsAllowed = true )
255- .map { (untpdTree, tpdTree) =>
256- val file = SourceFile .virtual( " <completions> " , expr, maybeIncomplete = true )
257- val unit = CompilationUnit (file)( using state.context )
258- unit.untpdTree = untpdTree
259- unit.tpdTree = tpdTree
260- given Context = state.context.fresh.setCompilationUnit(unit)
261- val srcPos = SourcePosition (file, Span (cursor) )
262- val completions = try Completion .completions(srcPos)._2 catch case NonFatal (_) => Nil
263- completions.map(_.label).distinct.map(makeCandidate)
264- }
265- .getOrElse(Nil )
268+ Right :
269+ compiler
270+ .typeCheck(expr, errorsAllowed = true )
271+ .map { (untpdTree, tpdTree) =>
272+ val file = SourceFile .virtual( " <completions> " , expr, maybeIncomplete = true )
273+ val unit = CompilationUnit (file)( using state.context)
274+ unit.untpdTree = untpdTree
275+ unit.tpdTree = tpdTree
276+ given Context = state.context.fresh.setCompilationUnit(unit )
277+ val srcPos = SourcePosition (file, Span (cursor))
278+ try Completion .completions(srcPos)._2 catch case NonFatal (_) => Nil
279+ }
280+ .getOrElse(Nil )
266281 end completions
267282
268283 protected def interpret (res : ParseResult , quiet : Boolean = false )(using state : State ): State = {
0 commit comments