From 61cc4ae11db65a2526e2cac4382326a27228a24b Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 19 Dec 2024 23:37:45 +0100 Subject: [PATCH] Add named pattern completions --- .../tools/dotc/interactive/Completion.scala | 48 +-- .../dotty/tools/pc/InferExpectedType.scala | 2 - .../src/main/dotty/tools/pc/PcCollector.scala | 8 - .../dotty/tools/pc/PcReferencesProvider.scala | 1 - .../tools/pc/ScalaPresentationCompiler.scala | 2 - .../tools/pc/SymbolInformationProvider.scala | 1 - .../pc/completions/CompletionValue.scala | 4 +- .../tools/pc/completions/Completions.scala | 2 + .../pc/completions/MatchCaseCompletions.scala | 1 - .../completions/NamedPatternCompletions.scala | 104 ++++++ .../tools/pc/tests/CompilerCachingSuite.scala | 1 - .../pc/tests/InferExpectedTypeSuite.scala | 1 - .../CompletionNamedPatternSuite.scala | 304 ++++++++++++++++++ .../SignatureHelpInterleavingSuite.scala | 1 - .../pc/utils/TestingWorkspaceSearch.scala | 1 - 15 files changed, 441 insertions(+), 40 deletions(-) create mode 100644 presentation-compiler/src/main/dotty/tools/pc/completions/NamedPatternCompletions.scala create mode 100644 presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionNamedPatternSuite.scala diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index ff5716b227ca..404679676fb4 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -89,21 +89,24 @@ object Completion: * * Otherwise, provide no completion suggestion. */ - def completionMode(path: List[untpd.Tree], pos: SourcePosition): Mode = path match - case GenericImportSelector(sel) => - if sel.imported.span.contains(pos.span) then Mode.ImportOrExport // import scala.@@ - 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.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 - - if (ref.name.isTermName) Mode.Term | maybeSelectMembers - else if (ref.name.isTypeName) Mode.Type | maybeSelectMembers - else Mode.None - - case _ => Mode.None + def completionMode(path: List[untpd.Tree], pos: SourcePosition): Mode = + path match + case GenericImportSelector(sel) => + if sel.imported.span.contains(pos.span) then Mode.ImportOrExport // import scala.@@ + 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 BindMixedWithNamedPatterns() => Mode.None // case User(name = name, sur@@) + case untpd.Literal(Constants.Constant(_: String)) :: _ => Mode.Term | Mode.Scope // literal completions + // TODO case (_: tpd.Bind) :: _ => we should complete only when in backticks + case (ref: untpd.RefTree) :: _ => + val maybeSelectMembers = if ref.isInstanceOf[untpd.Select] then Mode.Member else Mode.Scope + + if (ref.name.isTermName) Mode.Term | maybeSelectMembers + else if (ref.name.isTypeName) Mode.Type | maybeSelectMembers + else Mode.None + + case _ => Mode.None /** When dealing with in varios palces we check to see if they are * due to incomplete backticks. If so, we ensure we get the full prefix @@ -161,12 +164,19 @@ object Completion: case (sel: untpd.ImportSelector) :: _ => Some(sel) case _ => None + private object BindMixedWithNamedPatterns: + def unapply(path: List[untpd.Tree]): Boolean = + path match + case (_: untpd.Ident) :: (fn0: untpd.Apply) :: untpd.CaseDef(fn1, _, _) :: _ + if fn1 == fn0 && fn0.args.exists(_.isInstanceOf[untpd.NamedArg]) => true + case _ => false + private object GenericImportOrExport: - def unapply(path: List[untpd.Tree]): Option[untpd.ImportOrExport] = + def unapply(path: List[untpd.Tree]): Boolean = path match - case untpd.Ident(_) :: (importOrExport: untpd.ImportOrExport) :: _ => Some(importOrExport) - case (importOrExport: untpd.ImportOrExport) :: _ => Some(importOrExport) - case _ => None + case untpd.Ident(_) :: (importOrExport: untpd.ImportOrExport) :: _ => true + case (importOrExport: untpd.ImportOrExport) :: _ => true + case _ => false /** Inspect `path` to determine the offset where the completion result should be inserted. */ def completionOffset(untpdPath: List[untpd.Tree]): Int = diff --git a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala index 3d65f69621e1..d2a004075196 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/InferExpectedType.scala @@ -1,6 +1,5 @@ package dotty.tools.pc -import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.Context @@ -16,7 +15,6 @@ import dotty.tools.dotc.typer.Applications.UnapplyArgs import dotty.tools.dotc.util.NoSourcePosition import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.util.Spans.Span -import dotty.tools.pc.IndexedContext import dotty.tools.pc.printer.ShortenedTypePrinter import dotty.tools.pc.printer.ShortenedTypePrinter.IncludeDefaultParam import dotty.tools.pc.utils.InteractiveEnrichments.* diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala b/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala index 1ebfd405768e..e27b385a9547 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcCollector.scala @@ -1,30 +1,22 @@ package dotty.tools.pc -import java.nio.file.Paths import dotty.tools.pc.PcSymbolSearch.* -import scala.meta.internal.metals.CompilerOffsetParams import scala.meta.pc.OffsetParams import scala.meta.pc.VirtualFileParams import scala.meta as m -import dotty.tools.dotc.ast.NavigateAST -import dotty.tools.dotc.ast.Positioned import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.ast.untpd -import dotty.tools.dotc.ast.untpd.ExtMethods import dotty.tools.dotc.ast.untpd.ImportSelector import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.NameOps.* import dotty.tools.dotc.core.Names.* -import dotty.tools.dotc.core.StdNames.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* -import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver -import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.Spans.Span import dotty.tools.pc.utils.InteractiveEnrichments.* diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcReferencesProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcReferencesProvider.scala index 49ed313faec4..83986b8ad555 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcReferencesProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcReferencesProvider.scala @@ -8,7 +8,6 @@ import scala.meta.internal.metals.CompilerOffsetParams import scala.meta.pc.ReferencesRequest import scala.meta.pc.ReferencesResult -import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.interactive.InteractiveDriver diff --git a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala index 218d92c38ffa..3a299c01e3e9 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala @@ -30,10 +30,8 @@ import scala.meta.pc.{PcSymbolInformation as IPcSymbolInformation} import dotty.tools.dotc.reporting.StoreReporter import dotty.tools.pc.completions.CompletionProvider -import dotty.tools.pc.InferExpectedType import dotty.tools.pc.completions.OverrideCompletions import dotty.tools.pc.buildinfo.BuildInfo -import dotty.tools.pc.SymbolInformationProvider import dotty.tools.dotc.interactive.InteractiveDriver import org.eclipse.lsp4j.DocumentHighlight diff --git a/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala index ccda618078b8..87d810b599fa 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala @@ -12,7 +12,6 @@ import dotty.tools.dotc.core.Names.* import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.Symbols.* import dotty.tools.pc.utils.InteractiveEnrichments.deepDealias -import dotty.tools.pc.SemanticdbSymbols import dotty.tools.pc.utils.InteractiveEnrichments.allSymbols import dotty.tools.pc.utils.InteractiveEnrichments.stripBackticks import scala.meta.internal.pc.PcSymbolInformation diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala index 90b285bffb3a..f350e74e4ce6 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala @@ -250,7 +250,7 @@ object CompletionValue: denotation: Denotation ) extends Symbolic: override def insertText: Option[String] = Some(label.replace("$", "$$").nn) - override def completionItemDataKind: Integer = CompletionSource.OverrideKind.ordinal + override def completionItemDataKind: Integer = CompletionSource.NamedArgKind.ordinal override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Field override def description(printer: ShortenedTypePrinter)(using Context): String = @@ -265,7 +265,7 @@ object CompletionValue: ) extends CompletionValue: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Enum - override def completionItemDataKind: Integer = CompletionSource.OverrideKind.ordinal + override def completionItemDataKind: Integer = CompletionSource.AutoFillKind.ordinal override def insertText: Option[String] = Some(value) override def label: String = "Autofill with default values" diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala index 05dbe1ef5a43..2c8b30073e43 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -336,6 +336,8 @@ class Completions( val values = ScaladocCompletions.contribute(pos, text, config) (values, true) + case NamedPatternCompletions(namedPatternCompletions) => (namedPatternCompletions(completionPos), false) + case MatchCaseExtractor.MatchExtractor(selector) => ( CaseKeywordCompletion.matchContribute( diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala index 2efcba48e82d..6315a3f608f6 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala @@ -27,7 +27,6 @@ import dotty.tools.dotc.core.Types.NoType import dotty.tools.dotc.core.Types.OrType import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.core.Types.TypeRef -import dotty.tools.dotc.core.Types.AppliedType import dotty.tools.dotc.typer.Applications.UnapplyArgs import dotty.tools.dotc.util.SourcePosition import dotty.tools.pc.AutoImports.AutoImportsGenerator diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedPatternCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedPatternCompletions.scala new file mode 100644 index 000000000000..bfa01cd128e1 --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedPatternCompletions.scala @@ -0,0 +1,104 @@ +package dotty.tools.pc.completions + +import dotty.tools.dotc.ast.tpd.* +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.StdNames.* +import dotty.tools.dotc.core.SymDenotations.NoDenotation +import dotty.tools.dotc.core.Symbols +import dotty.tools.dotc.core.Symbols.defn +import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Types.* +import dotty.tools.dotc.util.SourcePosition +import scala.meta.internal.pc.CompletionFuzzy + +object NamedPatternCompletions: + + def isInsideParams(sourcePos: SourcePosition, start: Int): Boolean = + sourcePos.source.content().slice(sourcePos.start, start).foldLeft(0): (count, char) => + if char == '(' then count + 1 + else if char == ')' then count - 1 + else count + > 0 + + def unapply(path: List[Tree])(using Context): Option[CompletionPos => List[CompletionValue]] = + val result = path match + // case (nam@@ + // but not case nam@@ + case (bind: Bind) :: (caseDef: CaseDef) :: Match(selector, _) :: _ + if isInsideParams(caseDef.sourcePos, bind.sourcePos.end) => + if selector.tpe.widenDealias.isNamedTupleType then + Some(selector.tpe.widenDealias.namedTupleElementTypes.toMap, Nil) + else None + + // case (name = supername, na@@ + // case (nam@@, surname = test) => + case (_: Bind) :: (rest @ (unapply: UnApply) :: _) + if defn.isTupleClass(unapply.fun.symbol.owner.companionClass) => + rest.collectFirst: // We can't complete names without knowing the type of selector + case Match(selector, _) => selector + .flatMap: selector => + if selector.tpe.widenDealias.isNamedTupleType then + Some(selector.tpe.widenDealias.namedTupleElementTypes.toMap, unapply.patterns) + else None + + // case User(nam@@ + // case User(nam@@, surname = test) => + case (_: Bind) :: (rest @ (unapply: UnApply) :: _) => + Some(unapplyResultNamesToTypes(unapply.fun), unapply.patterns) + + // This case is happening because nam@@ is removed at desugaring as it is illegal unnamed bind mixed with named one + // case User(surname = test, nam@@) => + // case User(surname = test, nam@@ + case UnApply(fun, _, patterns) :: _ => Some(unapplyResultNamesToTypes(fun), patterns) + case _ => None + + result.map: (namesToArgs, patterns) => + contribute(_, namesToArgs, patterns) + end unapply + + private object NamedTupleUnapplyResultType: + def unapply(tree: Type)(using Context): Option[Type] = tree match + case AppliedType(TypeRef(_, cls), (namedTuple @ defn.NamedTuple(_, _)) :: Nil) + if (cls == ctx.definitions.OptionClass || cls == ctx.definitions.SomeClass) => Some(namedTuple) + case _ => None + + private def unapplyResultNamesToTypes(tree: Tree)(using Context): Map[Name, Type] = + tree.tpe.widenDealias.finalResultType match + // result type is named tuple, we can directly extract names + case AppliedType(TypeRef(_, cls), (namedTuple @ defn.NamedTuple(_, _)) :: Nil) + if (cls == ctx.definitions.OptionClass || cls == ctx.definitions.SomeClass) => + namedTuple.namedTupleElementTypes.toMap + // unapplies generated for case classes have synthetic names and result type is not a named tuple + case _ if tree.symbol.flags.is(Flags.Synthetic) => + val apply = tree.symbol.owner.info.member(nme.apply) + val maybeApplied = tree match + // The check for case flag is necessary to filter introduced type bounds T$1..n + case tpeApply @ TypeApply(_, args) if !args.exists(_.symbol.flags.is(Flags.Case)) => + apply.info.appliedTo(args.map(_.tpe)) + case _ => apply.info + + val unapplyParamList = maybeApplied.paramNamess.indexWhere(_.forall(_.isTermName)) + if unapplyParamList < 0 then Map.empty + else + val paramNames= maybeApplied.paramNamess(unapplyParamList) + val paramInfos = maybeApplied.paramInfoss(unapplyParamList) + (paramNames zip paramInfos).toMap + case _ => Map.empty // we can't help complete non synthetic non named tuple extractors + + def contribute( + completionPos: CompletionPos, + namesToArgs: Map[Name, Type], + patterns: List[Tree] + )(using Context): List[CompletionValue] = + val usedNames = patterns.collect: + case NamedArg(name, _) => name.asTermName + + val remainingParams = namesToArgs -- usedNames + remainingParams + .toList + .filter: (name, _) => + CompletionFuzzy.matchesSubCharacters(completionPos.query, name.toString) + .map: (name, tpe) => + CompletionValue.NamedArg(name.show + " = ", tpe, NoDenotation) + diff --git a/presentation-compiler/test/dotty/tools/pc/tests/CompilerCachingSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/CompilerCachingSuite.scala index 5e13c07b9e5f..8de7aec7d0a1 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/CompilerCachingSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/CompilerCachingSuite.scala @@ -11,7 +11,6 @@ import scala.meta.internal.metals.CompilerOffsetParams import scala.meta.pc.OffsetParams import scala.concurrent.Future import scala.concurrent.Await -import scala.meta.pc.VirtualFileParams import scala.concurrent.duration.* import java.util.Collections diff --git a/presentation-compiler/test/dotty/tools/pc/tests/InferExpectedTypeSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/InferExpectedTypeSuite.scala index ccdc68ef1cad..9d36ef90a30f 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/InferExpectedTypeSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/InferExpectedTypeSuite.scala @@ -9,7 +9,6 @@ import dotty.tools.pc.ScalaPresentationCompiler import scala.meta.internal.mtags.CommonMtagsEnrichments.* import org.junit.Test -import org.junit.Ignore class InferExpectedTypeSuite extends BasePCSuite: def check( diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionNamedPatternSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionNamedPatternSuite.scala new file mode 100644 index 000000000000..4c77a1d85edf --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionNamedPatternSuite.scala @@ -0,0 +1,304 @@ +package dotty.tools.pc.tests.completion + +import dotty.tools.pc.base.BaseCompletionSuite + +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runners.MethodSorters + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class CompletionNamedPatternSuite extends BaseCompletionSuite: + + @Test def `named-tuples-1` = + check( + """ + |import scala.language.experimental.namedTuples + | + |type User = (id: Int, name: String, surname: String) + |val user = (id = 5, name = "Bob", surname = "Marley") + | + |def idsWithName(name: String) = user match + | case (nam@@ + |""".stripMargin, + """name = : String + |surname = : String""".stripMargin + ) + + @Test def `named-tuples-1-without-parens` = + check( + """ + |import scala.language.experimental.namedTuples + | + |type User = (id: Int, name: String, surname: String) + |val user = (id = 5, name = "Bob", surname = "Marley") + | + |def idsWithName(name: String) = user match + | case nam@@ + |""".stripMargin, + "" + ) + + + @Test def `named-tuples-2` = + check( + """ + |import scala.language.experimental.namedTuples + | + |type User = (id: Int, name: String, surname: String) + |val user = (id = 5, name = "Bob", surname = "Marley") + | + |def idsWithName(name: String) = user match + | case (name = supername, na@@ + |""".stripMargin, + """name = : String + |surname = : String""".stripMargin + ) + + @Test def `named-tuples-3` = + check( + """ + |import scala.language.experimental.namedTuples + | + |type User = (id: Int, name: String, surname: String) + |val user = (id = 5, name = "Bob", surname = "Marley") + | + |def idsWithName(name: String) = user match + | case (na@@, name = name) => + |""".stripMargin, + """name = : String + |surname = : String""".stripMargin + ) + + @Test def `named-tuples-synthetic` = + check( + """ + |import scala.language.experimental.namedTuples + | + |case class User(id: Int, name: String, surname: String) + | + |extension (values: Seq[User]) + | // Collect user IDs of every entry that has the name matching argument + | def idsWithName(name: String) = values.collect: + | case User(name = `name`, id = userId) => userId + | case User(nam@@ + |""".stripMargin, + """name = : String + |surname = : String""".stripMargin + ) + + @Test def `named-tuples-custom-extractor` = + check( + """ + |import scala.language.experimental.namedTuples + | + |object MegaUser: + | def unapply(x: User): Option[(firstName: String, surname: String)] = ??? + |case class User(id: Int, name: String, surname: String) + | + |extension (values: Seq[User]) + | // Collect user IDs of every entry that has the name matching argument + | def idsWithName(name: String) = values.collect: + | case User(name = `name`, id = userId) => userId + | case MegaUser(nam@@ + |""".stripMargin, + """firstName = : String + |surname = : String""".stripMargin + ) + + @Test def `named-tuples-implicit-synthetic` = + check( + """ + |import scala.language.experimental.namedTuples + | + |case class User(id: Int, name: String, surname: String)(using String) + | + |extension (values: Seq[User]) + | // Collect user IDs of every entry that has the name matching argument + | def idsWithName(name: String) = values.collect: + | case User(name = `name`, id = userId) => userId + | case User(nam@@ + |""".stripMargin, + """name = : String + |surname = : String""".stripMargin + ) + + @Test def `named-tuples-type-param-synthetic` = + check( + """ + |import scala.language.experimental.namedTuples + | + |case class User[T](id: Int, name: String, surname: String, name2: T) + | + |extension (values: Seq[User[_]]) + | // Collect user IDs of every entry that has the name matching argument + | def idsWithName(name: String) = values.collect: + | case User(name = `name`, id = userId) => userId + | case User(nam@@ + |""".stripMargin, + """name = : String + |name2 = : T + |surname = : String""".stripMargin + ) + + @Test def `named-tuples-type-param-synthetic-concrete` = + check( + """ + |import scala.language.experimental.namedTuples + | + |case class User[T](id: Int, name: String, surname: String, name2: T) + | + |extension (values: Seq[User[Int]]) + | // Collect user IDs of every entry that has the name matching argument + | def idsWithName(name: String) = values.collect: + | case User(name = `name`, id = userId) => userId + | case User(nam@@ + |""".stripMargin, + """name = : String + |name2 = : Int + |surname = : String""".stripMargin + ) + + @Test def `named-tuples-bind-with-named-patterns-1` = + check( + """ + |import scala.language.experimental.namedTuples + | + |case class User(id: Int, name: String, surname: String) + | + |extension (values: Seq[User]) + | // Collect user IDs of every entry that has the name matching argument + | def idsWithName(name: String) = values.collect: + | case User(surname = test, nam@@ + |""".stripMargin, + "name = : String" + ) + + @Test def `named-tuples-bind-with-named-patterns-2` = + check( + """ + |import scala.language.experimental.namedTuples + | + |case class User(id: Int, name: String, surname: String) + | + |extension (values: Seq[User]) + | // Collect user IDs of every entry that has the name matching argument + | def idsWithName(name: String) = values.collect: + | case User(surname = test, nam@@) => + |""".stripMargin, + "name = : String" + ) + + // TODO completion for binds but only in backtics + // @Test def `named-tuples-bind-with-named-patterns-3` = + // check( + // """ + // |import scala.language.experimental.namedTuples + // | + // |case class User(id: Int, name: String, surname: String) + // |val veryGoodName = "Bob" + // | + // |extension (values: Seq[User]) + // | // Collect user IDs of every entry that has the name matching argument + // | def idsWithName(name: String) = values.collect: + // | case User(surname = test, name = `ver@@ + // |""".stripMargin, + // "name = : String" + // ) + + @Test def `nested-unapply-in-named-tuples-1` = + check( + """ + |import scala.language.experimental.namedTuples + |type Person = (name: Option[String], age: Int) + | + |val person = (name = Some("Bob"), age = 33) + |def test = person match + | case (name = Som@@ + | + |""".stripMargin, + """Some(value) scala + |Some scala""".stripMargin + ) + + @Test def `nested-unapply-in-named-tuples-2` = + check( + """ + |import scala.language.experimental.namedTuples + |type Person = (name: Option[String], age: Int) + |type AnotherPerson = (name: Option[Person], age: Int) + | + |val person: Person = (name = Some("Bob"), age = 33) + |val anotherPerson = (name = Some(person), age = 33) + | + |def test = anotherPerson match + | case (name = Some((na@@ + | + |""".stripMargin, + "name = : Option[String]" + ) + + @Test def `nested-unapply-in-named-tuples-3` = + check( + """ + |import scala.language.experimental.namedTuples + |case class Person(name: Option[String], age: Int) + | + |val person = Person(name = Some("Bob"), age = 33) + |def test = person match + | case (name = Som@@ + | + |""".stripMargin, + """Some(value) scala + |Some scala + |Some scala""".stripMargin + ) + + @Test def `nested-unapply-in-named-tuples-4` = + check( + """ + |import scala.language.experimental.namedTuples + |case class Person(name: Option[String], age: Int) + |case class AnotherPerson(name: Option[Person], age: Int) + | + |val person = Person(name = Some("Bob"), age = 33) + |val anotherPerson = AnotherPerson(name = Some(person), age = 33) + | + |def test = anotherPerson match + | case Person(name = Some(AnotherPers@@ + | + |""".stripMargin, + "AnotherPerson test" + //FIXME There should also be unapply for AnotherPerson(name: Option[Person], age: Int) but this is not a bug + // introduced in this commit + ) + + // TODO Leaving this test as I want to make it work in the future, same for named args + // @Test def `named-tuples-bind-with-named-patterns-2` = + // check( + // """ + // |import scala.language.experimental.namedTuples + // | + // |case class User(id: Int, name: String, surname: String) + // | + // |extension (values: Seq[User]) + // | // Collect user IDs of every entry that has the name matching argument + // | def idsWithName(name: String) = values.collect: + // | case User(nam@@ surname = test) => + // |""".stripMargin, + // "name = : String" + // ) + + @Test def `named-tuples-bind-with-named-patterns-4` = + check( + """ + |import scala.language.experimental.namedTuples + | + |case class User(id: Int, name: String, surname: String) + | + |extension (values: Seq[User]) + | // Collect user IDs of every entry that has the name matching argument + | def idsWithName(name: String) = values.collect: + | case User(nam@@, surname = test) => + |""".stripMargin, + "name = : String" + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpInterleavingSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpInterleavingSuite.scala index 735a2eb13fab..9c88f95be6fd 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpInterleavingSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/signaturehelp/SignatureHelpInterleavingSuite.scala @@ -4,7 +4,6 @@ import dotty.tools.pc.base.BaseSignatureHelpSuite import org.junit.Test import org.junit.Ignore -import java.nio.file.Path class SignatureHelpInterleavingSuite extends BaseSignatureHelpSuite: diff --git a/presentation-compiler/test/dotty/tools/pc/utils/TestingWorkspaceSearch.scala b/presentation-compiler/test/dotty/tools/pc/utils/TestingWorkspaceSearch.scala index 27b9a49f9555..0d30150acc5a 100644 --- a/presentation-compiler/test/dotty/tools/pc/utils/TestingWorkspaceSearch.scala +++ b/presentation-compiler/test/dotty/tools/pc/utils/TestingWorkspaceSearch.scala @@ -11,7 +11,6 @@ import java.io.File import java.nio.file.Paths import scala.collection.mutable import scala.language.unsafeNulls -import scala.meta.internal.metals.CompilerVirtualFileParams import scala.meta.internal.metals.Fuzzy import scala.meta.internal.metals.WorkspaceSymbolQuery import scala.meta.pc.SymbolSearchVisitor