From 67e00eae6a35fa6269502ff48416bf2c15afecb8 Mon Sep 17 00:00:00 2001 From: Nikita Glushchenko Date: Thu, 3 Apr 2025 19:29:16 +0200 Subject: [PATCH] Completions for requests just before string (#22894) fixes #22514 [Cherry-picked 823782c796e73c322212f7c4d94d6049434407aa] --- .../tools/dotc/interactive/Completion.scala | 15 +++- .../CompletionStringContextSuite.scala | 79 +++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionStringContextSuite.scala diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index ba37a48287ff..b1c0119fb2df 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -95,6 +95,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 @@ -169,6 +171,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 @@ -218,7 +228,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