From 21168b43ae86edf7ba3a4b1c5c8774b48b84d7bd Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Mon, 1 Feb 2021 21:30:49 +1000 Subject: [PATCH] Fix completion of keywored-starting-idents (e.g. formatted) and add CTRL-T type/tree printer --- .../tools/nsc/interpreter/jline/Reader.scala | 95 +++++++++++++++---- .../nsc/interpreter/shell/Completion.scala | 4 +- .../tools/nsc/interpreter/shell/ILoop.scala | 6 +- .../nsc/interpreter/shell/LoopCommands.scala | 4 +- .../interpreter/shell/ReplCompletion.scala | 8 +- .../scala/tools/nsc/interpreter/IMain.scala | 11 ++- .../tools/nsc/interpreter/Interface.scala | 7 +- .../interpreter/PresentationCompilation.scala | 95 ++++++++++--------- .../nsc/interpreter/CompletionTest.scala | 12 +-- 9 files changed, 151 insertions(+), 91 deletions(-) diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala index 8fac4ce94deb..56ee9bf477f0 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/jline/Reader.scala @@ -13,15 +13,21 @@ package scala.tools.nsc.interpreter package jline -import java.util.{List => JList} -import org.jline.reader.{Candidate, Completer, CompletingParsedLine, EOFError, EndOfFileException, History, LineReader, ParsedLine, Parser, SyntaxError, UserInterruptException} +import java.util.{Collections, List => JList} +import org.jline.reader.{Candidate, Completer, CompletingParsedLine, EOFError, EndOfFileException, History, LineReader, ParsedLine, Parser, Reference, SyntaxError, UserInterruptException} import org.jline.reader.impl.{CompletionMatcherImpl, DefaultParser, LineReaderImpl} import org.jline.terminal.Terminal import shell.{Accumulator, ShellConfig} import Parser.ParseContext +import org.jline.console.{CmdDesc, CmdLine} +import org.jline.keymap.KeyMap +import org.jline.utils.AttributedString +import org.jline.widget.TailTipWidgets.TipType +import org.jline.widget.{TailTipWidgets} import java.{lang, util} +import scala.reflect.internal.Chars /** A Reader that delegates to JLine3. */ @@ -95,21 +101,67 @@ object Reader { .option(Option.DISABLE_EVENT_EXPANSION, true) // Otherwise `scala> println(raw"\n".toList)` gives `List(n)` !! .option(Option.COMPLETE_MATCHER_CAMELCASE, true) } - builder.completionMatcher(new CompletionMatcherImpl { + object customCompletionMatcher extends CompletionMatcherImpl { override def compile(options: util.Map[LineReader.Option, lang.Boolean], prefix: Boolean, line: CompletingParsedLine, caseInsensitive: Boolean, errors: Int, originalGroupName: String): Unit = { super.compile(options, prefix, line, caseInsensitive, errors, originalGroupName) // TODO Use Option.COMPLETION_MATCHER_TYPO(false) in once https://github.com/jline/jline3/pull/646 matchers.remove(matchers.size() - 2) - // TODO add SNAKE_CASE completion matcher. } - }) + + override def matches(candidates: JList[Candidate]): JList[Candidate] = { + val matching = super.matches(candidates) + matching + } + } + + builder.completionMatcher(customCompletionMatcher) val reader = builder.build() + + val desc: java.util.function.Function[CmdLine, CmdDesc] = (cmdLine) => new CmdDesc(util.Arrays.asList(new AttributedString("demo")), Collections.emptyList(), Collections.emptyMap()) + new TailTipWidgets(reader, desc, 1, TipType.COMPLETER) + val keyMap = reader.getKeyMaps.get("main") + + object ScalaShowType { + val Name = "scala-show-type" + private var lastInvokeLocation: Option[(String, Int)] = None + def apply(): Boolean = { + val nextInvokeLocation = Some((reader.getBuffer.toString, reader.getBuffer.cursor())) + val cursor = reader.getBuffer.cursor() + val text = reader.getBuffer.toString + val result = completer.complete(text, cursor, filter = true) + if (lastInvokeLocation == nextInvokeLocation) { + showTree(result) + lastInvokeLocation = None + } else { + showType(result) + lastInvokeLocation = nextInvokeLocation + } + true + } + def showType(result: shell.CompletionResult): Unit = { + reader.getTerminal.writer.println() + reader.getTerminal.writer.println(result.typeAtCursor) + reader.callWidget(LineReader.REDRAW_LINE) + reader.callWidget(LineReader.REDISPLAY) + reader.getTerminal.flush() + } + def showTree(result: shell.CompletionResult): Unit = { + reader.getTerminal.writer.println() + reader.getTerminal.writer.println(Naming.unmangle(result.typedTree)) + reader.callWidget(LineReader.REDRAW_LINE) + reader.callWidget(LineReader.REDISPLAY) + reader.getTerminal.flush() + } + } + reader.getWidgets().put(ScalaShowType.Name, () => ScalaShowType()) + locally { import LineReader._ // VIINS, VICMD, EMACS val keymap = if (config.viMode) VIINS else EMACS reader.getKeyMaps.put(MAIN, reader.getKeyMaps.get(keymap)); + keyMap.bind(new Reference(ScalaShowType.Name), KeyMap.ctrl('T')) } def secure(p: java.nio.file.Path): Unit = { try scala.reflect.internal.util.OwnerOnlyChmod.chmodFileOrCreateEmpty(p) @@ -177,6 +229,12 @@ object Reader { val (wordCursor, wordIndex) = current match { case Some(t) if t.isIdentifier => (cursor - t.start, tokens.indexOf(t)) + case Some(t) => + val isIdentifierStartKeyword = (t.start until t.end).forall(i => Chars.isIdentifierPart(line.charAt(i))) + if (isIdentifierStartKeyword) + (cursor - t.start, tokens.indexOf(t)) + else + (0, -1) case _ => (0, -1) } @@ -233,6 +291,7 @@ object Reader { * It delegates both interfaces to an underlying `Completion`. */ class Completion(delegate: shell.Completion) extends shell.Completion with Completer { + var lastPrefix: String = "" require(delegate != null) // REPL Completion def complete(buffer: String, cursor: Int, filter: Boolean): shell.CompletionResult = delegate.complete(buffer, cursor, filter) @@ -240,8 +299,8 @@ class Completion(delegate: shell.Completion) extends shell.Completion with Compl // JLine Completer def complete(lineReader: LineReader, parsedLine: ParsedLine, newCandidates: JList[Candidate]): Unit = { def candidateForResult(cc: CompletionCandidate): Candidate = { - val value = cc.defString - val displayed = cc.defString + (cc.arity match { + val value = cc.name + val displayed = cc.name + (cc.arity match { case CompletionCandidate.Nullary => "" case CompletionCandidate.Nilary => "()" case _ => "(" @@ -257,23 +316,19 @@ class Completion(delegate: shell.Completion) extends shell.Completion with Compl new Candidate(value, displayed, group, descr, suffix, key, complete) } val result = complete(parsedLine.line, parsedLine.cursor, filter = false) - result.candidates.map(_.defString) match { - // the presence of the empty string here is a signal that the symbol - // is already complete and so instead of completing, we want to show - // the user the method signature. there are various JLine 3 features - // one might use to do this instead; sticking to basics for now - case "" :: defStrings if defStrings.nonEmpty => - // specifics here are cargo-culted from Ammonite + for (cc <- result.candidates) + newCandidates.add(candidateForResult(cc)) + + val parsedLineWord = parsedLine.word() + result.candidates.filter(_.name == parsedLineWord) match { + case Nil => + case exacts => lineReader.getTerminal.writer.println() - for (cc <- result.candidates.tail) - lineReader.getTerminal.writer.println(cc.defString) + for (cc <- exacts) + lineReader.getTerminal.writer.println(cc.declString()) lineReader.callWidget(LineReader.REDRAW_LINE) lineReader.callWidget(LineReader.REDISPLAY) lineReader.getTerminal.flush() - // normal completion - case _ => - for (cc <- result.candidates) - newCandidates.add(candidateForResult(cc)) } } } diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/Completion.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/Completion.scala index d3471e343162..a05fbeeac800 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/Completion.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/Completion.scala @@ -21,14 +21,14 @@ object NoCompletion extends Completion { def complete(buffer: String, cursor: Int, filter: Boolean) = NoCompletions } -case class CompletionResult(cursor: Int, candidates: List[CompletionCandidate]) { +case class CompletionResult(cursor: Int, candidates: List[CompletionCandidate], typeAtCursor: String = "", typedTree: String = "") { final def orElse(other: => CompletionResult): CompletionResult = if (candidates.nonEmpty) this else other } object CompletionResult { val empty: CompletionResult = NoCompletions } -object NoCompletions extends CompletionResult(-1, Nil) +object NoCompletions extends CompletionResult(-1, Nil, "", "") case class MultiCompletion(underlying: Completion*) extends Completion { override def complete(buffer: String, cursor: Int, filter: Boolean) = diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/ILoop.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/ILoop.scala index d33d42c54735..3021e1036e63 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/ILoop.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/ILoop.scala @@ -253,7 +253,7 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, val maybes = intp.visibleSettings.filter(x => if (filter) x.name.startsWith(s) else true).map(_.name) .filterNot(cond(_) { case "-"|"-X"|"-Y" => true }).sorted if (maybes.isEmpty) NoCompletions - else CompletionResult(cursor - s.length, maybes.map(CompletionCandidate(_))) + else CompletionResult(cursor - s.length, maybes.map(CompletionCandidate(_)), "", "") case _ => NoCompletions } } @@ -554,13 +554,13 @@ class ILoop(config: ShellConfig, inOverride: BufferedReader = null, // condition here is a bit weird because of the weird hack we have where // the first candidate having an empty defString means it's not really // completion, but showing the method signature instead - if (candidates.headOption.exists(_.defString.nonEmpty)) { + if (candidates.headOption.exists(_.name.nonEmpty)) { val prefix = if (completions == NoCompletions) "" else what.substring(0, completions.cursor) // hvesalai (emacs sbt-mode maintainer) says it's important to echo only once and not per-line echo( - candidates.map(c => s"[completions] $prefix${c.defString}") + candidates.map(c => s"[completions] $prefix${c.name}") .mkString("\n") ) } diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/LoopCommands.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/LoopCommands.scala index 829b35a025bb..f357a44c2fde 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/LoopCommands.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/LoopCommands.scala @@ -138,12 +138,12 @@ trait LoopCommands { val completion = ":" + cmd.name new Completion { def complete(buffer: String, cursor: Int, filter: Boolean) = - CompletionResult(cursor, List(CompletionCandidate(completion))) + CompletionResult(cursor, List(CompletionCandidate(completion)), "", "") } case cmd :: rest => new Completion { def complete(buffer: String, cursor: Int, filter: Boolean) = - CompletionResult(cursor, cmds.map(cmd => CompletionCandidate(":" + cmd.name))) + CompletionResult(cursor, cmds.map(cmd => CompletionCandidate(":" + cmd.name)), "", "") } } case _ => NoCompletion diff --git a/src/repl-frontend/scala/tools/nsc/interpreter/shell/ReplCompletion.scala b/src/repl-frontend/scala/tools/nsc/interpreter/shell/ReplCompletion.scala index f2ba331bc7cf..277781301441 100644 --- a/src/repl-frontend/scala/tools/nsc/interpreter/shell/ReplCompletion.scala +++ b/src/repl-frontend/scala/tools/nsc/interpreter/shell/ReplCompletion.scala @@ -51,16 +51,16 @@ class ReplCompletion(intp: Repl, val accumulator: Accumulator = new Accumulator) case Right(result) => try { buf match { case slashPrint() if cursor == buf.length => - CompletionResult(cursor, CompletionCandidate.fromStrings("" :: Naming.unmangle(result.print) :: Nil)) + CompletionResult(cursor, CompletionCandidate.fromStrings("" :: Naming.unmangle(result.print) :: Nil), "", "") case slashPrintRaw() if cursor == buf.length => - CompletionResult(cursor, CompletionCandidate.fromStrings("" :: result.print :: Nil)) + CompletionResult(cursor, CompletionCandidate.fromStrings("" :: result.print :: Nil), "", "") case slashTypeAt(start, end) if cursor == buf.length => - CompletionResult(cursor, CompletionCandidate.fromStrings("" :: result.typeAt(start.toInt, end.toInt) :: Nil)) + CompletionResult(cursor, CompletionCandidate.fromStrings("" :: result.typeAt(start.toInt, end.toInt) :: Nil), "", "") case _ => // under JLine 3, we no longer use the tabCount concept, so tabCount is always 1 // which always gives us all completions val (c, r) = result.completionCandidates(filter, tabCount = 1) - CompletionResult(c, r) + CompletionResult(c, r, result.typeAt(cursor, cursor), result.print) } } finally result.cleanup() } diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index a38b1075490b..27ac79c716c3 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -784,9 +784,12 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade // The source file contents only has the code originally input by the user, // with unit's body holding the synthetic trees. // When emitting errors, be careful not to refer to the synthetic code - private val unit = new CompilationUnit(new BatchSourceFile(if (synthetic) "" else label, line)) + // pad with a trailing " " so that the synthetic position for enclosing trees does not exactly coincide with the + // position of the user-written code, these seems to confuse the presentation compiler. + private val paddedLine = line + " " + private val unit = new CompilationUnit(new BatchSourceFile(if (synthetic) "" else label, paddedLine)) // a dummy position used for synthetic trees (needed for pres compiler to locate the trees for user input) - private val wholeUnit = Position.range(unit.source, 0, 0, line.length) + private val wholeUnit = Position.range(unit.source, 0, 0, paddedLine.length) private def storeInVal(tree: Tree): Tree = { val resName = newTermName(if (synthetic) freshInternalVarName() else freshUserVarName()) @@ -882,13 +885,13 @@ class IMain(val settings: Settings, parentClassLoaderOverride: Option[ClassLoade else ModuleDef(NoMods, readName, wrapperTempl)) if (isClassBased) - stats += q"""object $readName { val INSTANCE = new ${tq"""${readName.toTypeName}"""} }""" + stats += atPos(wholeUnit.focus)(q"""object $readName { val INSTANCE = new ${tq"""${readName.toTypeName}"""} }""") val unspliced = PackageDef(atPos(wholeUnit.focus)(Ident(lineRep.packageName)), stats.toList) unit.body = spliceUserCode.transform(unspliced) unit.encounteredXml(firstXmlPos) -// settings.Xprintpos.value = true + // settings.Xprintpos.value = true showCode(asCompactString(unit.body)) unit diff --git a/src/repl/scala/tools/nsc/interpreter/Interface.scala b/src/repl/scala/tools/nsc/interpreter/Interface.scala index 6a1a97c0dcd7..790750daf367 100644 --- a/src/repl/scala/tools/nsc/interpreter/Interface.scala +++ b/src/repl/scala/tools/nsc/interpreter/Interface.scala @@ -323,7 +323,7 @@ trait PresentationCompilationResult { def candidates(tabCount: Int): (Int, List[String]) = completionCandidates(tabCount) match { case (cursor, cands) => - (cursor, cands.map(_.defString)) + (cursor, cands.map(_.name)) } final def completionCandidates(tabCount: Int = -1): (Int, List[CompletionCandidate]) = completionCandidates(filter = true, tabCount) @@ -331,10 +331,11 @@ trait PresentationCompilationResult { } case class CompletionCandidate( - defString: String, + name: String, arity: CompletionCandidate.Arity = CompletionCandidate.Nullary, isDeprecated: Boolean = false, - isUniversal: Boolean = false) + isUniversal: Boolean = false, + declString: () => String = () => "") object CompletionCandidate { sealed trait Arity case object Nullary extends Arity diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala index d65ebe71de77..286f156fd605 100644 --- a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala @@ -12,7 +12,9 @@ package scala.tools.nsc.interpreter -import scala.reflect.internal.util.{Position, RangePosition, StringOps} +import scala.collection.mutable +import scala.reflect.internal.util.{Position, RangePosition} +import scala.tools.nsc.ast.parser.Tokens import scala.tools.nsc.backend.JavaPlatform import scala.tools.nsc.util.ClassPath import scala.tools.nsc.{Settings, interactive} @@ -34,7 +36,24 @@ trait PresentationCompilation { self: IMain => if (global == null) Left(Error) else { val pc = newPresentationCompiler() - val line1 = buf.patch(cursor, Cursor, 0) + def cursorIsInKeyword(): Boolean = { + val scanner = pc.newUnitParser(buf).newScanner() + scanner.init() + while (scanner.token != Tokens.EOF) { + val token = scanner.token + val o = scanner.offset + scanner.nextToken() + if ((o to scanner.lastOffset).contains(cursor)) { + return (!Tokens.isIdentifier(token) && pc.syntaxAnalyzer.token2name.contains(token)) + } + } + false + } + // Support completion of "def format = 42; for" by replacing the keyword with foo_CURSOR_ before + // typechecking. Only do this when needed to be able ot correctly return the type of `foo.bar` + // where `bar` is the complete name of a member. + val line1 = if (!cursorIsInKeyword()) buf else buf.patch(cursor, Cursor, 0) + val trees = pc.newUnitParser(line1).parseStats() val importer = global.mkImporter(pc) //println(s"pc: [[$line1]], <<${trees.size}>>") @@ -89,8 +108,6 @@ trait PresentationCompilation { self: IMain => interactiveGlobal } - private var lastCommonPrefixCompletion: Option[String] = None - abstract class PresentationCompileResult(val compiler: interactive.Global, val inputRange: Position, val cursor: Int, val buf: String) extends PresentationCompilationResult { val unit: compiler.RichCompilationUnit // depmet broken for constructors, can't be ctor arg @@ -161,65 +178,49 @@ trait PresentationCompilation { self: IMain => if (m.sym.paramss.isEmpty) CompletionCandidate.Nullary else if (m.sym.paramss.size == 1 && m.sym.paramss.head.isEmpty) CompletionCandidate.Nilary else CompletionCandidate.Other - def defStringCandidates(matching: List[Member], name: Name, isNew: Boolean): Candidates = { + def defStringCandidates(matching: List[Member], name: Name, isNew: Boolean) = { + val seen = new mutable.HashSet[Symbol]() val ccs = for { member <- matching - if member.symNameDropLocal == name + if seen.add(member.sym) sym <- if (member.sym.isClass && isNew) member.sym.info.decl(nme.CONSTRUCTOR).alternatives else member.sym.alternatives sugared = sym.sugaredSymbolOrSelf } yield { - val tp = member.prefix memberType sym CompletionCandidate( - defString = sugared.defStringSeenAs(tp), + name = member.symNameDropLocal.decoded, arity = memberArity(member), isDeprecated = isMemberDeprecated(member), - isUniversal = isMemberUniversal(member)) + isUniversal = isMemberUniversal(member), + declString = () => { + val tp = member.prefix memberType sym; sugared.defStringSeenAs(tp) + }) } - (cursor, CompletionCandidate("") :: ccs.distinct) + ccs } - def toCandidates(members: List[Member]): List[CompletionCandidate] = - members - .map(m => CompletionCandidate(m.symNameDropLocal.decoded, memberArity(m), isMemberDeprecated(m), isMemberUniversal(m))) - .sortBy(_.defString) val found = this.completionsAt(cursor) match { case NoResults => NoCandidates case r => def shouldHide(m: Member): Boolean = filter && tabCount == 0 && (isMemberDeprecated(m) || isMemberUniversal(m)) val matching = r.matchingResults(nameMatcher = if (filter) {entered => candidate => candidate.startsWith(entered)} else _ => _ => true).filterNot(shouldHide) - val tabAfterCommonPrefixCompletion = lastCommonPrefixCompletion.contains(buf.substring(inputRange.start, cursor)) && matching.exists(_.symNameDropLocal == r.name) - val doubleTab = tabCount > 0 && matching.forall(_.symNameDropLocal == r.name) - if (tabAfterCommonPrefixCompletion || doubleTab) { - val pos1 = positionOf(cursor) - import compiler._ - val locator = new Locator(pos1) - val tree = locator locateIn unit.body - var isNew = false - new TreeStackTraverser { - override def traverse(t: Tree): Unit = { - if (t eq tree) { - isNew = path.dropWhile { case _: Select | _: Annotated => true; case _ => false}.headOption match { - case Some(_: New) => true - case _ => false - } - } else super.traverse(t) - } - }.traverse(unit.body) - defStringCandidates(matching, r.name, isNew) - } else if (matching.nonEmpty && matching.forall(_.symNameDropLocal == r.name)) - NoCandidates // don't offer completion if the only option has been fully typed already - else { - val candidates = toCandidates(matching) - val pos = cursor - r.positionDelta - lastCommonPrefixCompletion = - if (buf.length >= pos) - Some(buf.substring(inputRange.start, pos) + StringOps.longestCommonPrefix(toCandidates(r.matchingResults()).map(_.defString))) - else - None - - // regular completion - (pos, candidates) - } + val pos1 = positionOf(cursor) + import compiler._ + val locator = new Locator(pos1) + val tree = locator locateIn unit.body + var isNew = false + new TreeStackTraverser { + override def traverse(t: Tree): Unit = { + if (t eq tree) { + isNew = path.dropWhile { case _: Select | _: Annotated => true; case _ => false}.headOption match { + case Some(_: New) => true + case _ => false + } + } else super.traverse(t) + } + }.traverse(unit.body) + val candidates = defStringCandidates(matching, r.name, isNew) + val pos = cursor - r.positionDelta + (pos, candidates) } found } diff --git a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala index 33bdcf401c47..f521d4ba43b5 100644 --- a/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala +++ b/test/junit/scala/tools/nsc/interpreter/CompletionTest.scala @@ -85,7 +85,7 @@ class CompletionTest { checkExact(completer, "asInstanceO", "", includeUniversal = true)("asInstanceOf") // Output is sorted - assertEquals(List("prefix_aaa", "prefix_nnn", "prefix_zzz"), completer.complete( """class C { def prefix_nnn = 0; def prefix_zzz = 0; def prefix_aaa = 0; prefix_""").candidates.filter(!_.isUniversal).map(_.defString)) + assertEquals(List("prefix_aaa", "prefix_nnn", "prefix_zzz"), completer.complete( """class C { def prefix_nnn = 0; def prefix_zzz = 0; def prefix_aaa = 0; prefix_""").candidates.filter(!_.isUniversal).map(_.name)) // Enable implicits to check completion enrichment checkExact(completer, """'c'.toU""")("toUpper") @@ -191,7 +191,7 @@ class CompletionTest { | .map(_ + 1) /* then we do reverse */ | .rev""".stripMargin assertTrue( - completer.complete(withMultilineCommit).candidates.map(_.defString).contains("reverseMap") + completer.complete(withMultilineCommit).candidates.map(_.name).contains("reverseMap") ) val withInlineCommit = @@ -199,7 +199,7 @@ class CompletionTest { | .map(_ + 1) // then we do reverse | .rev""".stripMargin assertTrue( - completer.complete(withInlineCommit).candidates.map(_.defString).contains("reverseMap") + completer.complete(withInlineCommit).candidates.map(_.name).contains("reverseMap") ) } @@ -233,9 +233,9 @@ class CompletionTest { """object A { class Type; object Term }""" ) val candidates1 = completer.complete("A.T").candidates - assertEquals("Term", candidates1.map(_.defString).mkString(" ")) + assertEquals("Term", candidates1.map(_.name).mkString(" ")) val candidates2 = completer.complete("import A.T").candidates - assertEquals("Term Type", candidates2.map(_.defString).sorted.mkString(" ")) + assertEquals("Term Type", candidates2.map(_.name).sorted.mkString(" ")) } @Test @@ -284,7 +284,7 @@ object Test2 { val actual = completer.complete(before, after).candidates .filter(c => includeUniversal || !c.isUniversal) - .map(_.defString) + .map(_.name) assertEquals(expected.sorted.mkString(" "), actual.toSeq.distinct.sorted.mkString(" ")) } }