diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index e59a8e0b882d..cef5de613920 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -97,6 +97,8 @@ object Completion: else if sel.isGiven && sel.bound.span.contains(pos.span) then Mode.ImportOrExport else Mode.None // import scala.{util => u@@} case GenericImportOrExport(_) => Mode.ImportOrExport | Mode.Scope // import TrieMa@@ + case untpd.InterpolatedString(_, untpd.Literal(Constants.Constant(_: String)) :: _) :: _ => + Mode.Term | Mode.Scope case untpd.Literal(Constants.Constant(_: String)) :: _ => Mode.Term | Mode.Scope // literal completions case (ref: untpd.RefTree) :: _ => val maybeSelectMembers = if ref.isInstanceOf[untpd.Select] then Mode.Member else Mode.Scope @@ -171,6 +173,14 @@ object Completion: case (importOrExport: untpd.ImportOrExport) :: _ => Some(importOrExport) case _ => None + private object StringContextApplication: + def unapply(path: List[tpd.Tree]): Option[tpd.Apply] = + path match + case tpd.Select(qual @ tpd.Apply(tpd.Select(tpd.Select(_, StdNames.nme.StringContext), _), _), _) :: _ => + Some(qual) + case _ => None + + /** Inspect `path` to determine the offset where the completion result should be inserted. */ def completionOffset(untpdPath: List[untpd.Tree]): Int = untpdPath match @@ -221,7 +231,10 @@ object Completion: // Ignore synthetic select from `This` because in code it was `Ident` // See example in dotty.tools.languageserver.CompletionTest.syntheticThis case tpd.Select(qual @ tpd.This(_), _) :: _ if qual.span.isSynthetic => completer.scopeCompletions - case tpd.Select(qual, _) :: _ if qual.typeOpt.hasSimpleKind => completer.selectionCompletions(qual) + case StringContextApplication(qual) => + completer.scopeCompletions ++ completer.selectionCompletions(qual) + case tpd.Select(qual, _) :: _ if qual.typeOpt.hasSimpleKind => + completer.selectionCompletions(qual) case tpd.Select(qual, _) :: _ => Map.empty case (tree: tpd.ImportOrExport) :: _ => completer.directMemberCompletions(tree.expr) case _ => completer.scopeCompletions diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionStringContextSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionStringContextSuite.scala new file mode 100644 index 000000000000..689be1862bcc --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionStringContextSuite.scala @@ -0,0 +1,79 @@ +package dotty.tools.pc.tests.completion + +import dotty.tools.pc.base.BaseCompletionSuite +import org.junit.Test +import dotty.tools.dotc.core.Types.ThisType.raw + +class CompletionStringContextSuite extends BaseCompletionSuite: + @Test + def inScopeSymbol = check( + """ + |object M: + | val VersionRegex = "".r + | VersionRe@@"1234" + """.stripMargin, + "|VersionRegex: Regex".stripMargin + ) + + @Test + def workspaceSymbol = check( + """ + |object M: + | ListBuf@@"1234" + """.stripMargin, + """ + |ListBuffer[A](elems: A*): ListBuffer[A] - scala.collection.mutable + |new ListBuffer[A]: ListBuffer[A] - scala.collection.mutable + |ListBuffer - scala.collection.mutable + |""".stripMargin + ) + + @Test + def providedSymbol = check( + """ + |object M: + | ra@@"1234" + """.stripMargin, + "|raw(args: Any*): String".stripMargin + ) + + // bellow are tests of edits + @Test + def editTest1 = checkEdit( + """ + |object M: + | ra@@"1234" + """.stripMargin, + """ + |object M: + | raw"1234" + |""".stripMargin + ) + + @Test + def editTest2 = checkEdit( + """ + |object M: + | printl@@"1234" + """.stripMargin, + """ + |object M: + | println"1234" + |""".stripMargin, + assertSingleItem = false + ) + + @Test + def editTest3 = checkEdit( + """ + |object M: + | def select(s: String): String = s + | selec@@"1234" + """.stripMargin, + """ + |object M: + | def select(s: String): String = s + | select"1234" + |""".stripMargin, + assertSingleItem = false + ) \ No newline at end of file