From 066f15a2cca740d3eff6e9659973be04d8df548e Mon Sep 17 00:00:00 2001 From: rochala Date: Wed, 13 Mar 2024 10:05:48 +0100 Subject: [PATCH 01/10] Stabilize deduplication of imports, add missing apply and constructors for classes --- .../tools/dotc/interactive/Completion.scala | 35 ++--- .../tools/languageserver/CompletionTest.scala | 23 +++ ...tionSuffix.scala => CompletionAffix.scala} | 32 ++-- .../pc/completions/CompletionProvider.scala | 14 +- .../pc/completions/CompletionValue.scala | 26 ++-- .../tools/pc/completions/Completions.scala | 144 +++++++++++------- .../completions/InterpolatorCompletions.scala | 4 +- 7 files changed, 174 insertions(+), 104 deletions(-) rename presentation-compiler/src/main/dotty/tools/pc/completions/{CompletionSuffix.scala => CompletionAffix.scala} (58%) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 025a2022500d..c51593f2a159 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -86,26 +86,21 @@ object Completion: * * Otherwise, provide no completion suggestion. */ - def completionMode(path: List[untpd.Tree], pos: SourcePosition): Mode = - - val completionSymbolKind: 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 - - completionSymbolKind + 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 /** 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 diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index d0ceb37c07ba..e770a2195dff 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -1721,4 +1721,27 @@ class CompletionTest { .completion(m1, Set( ("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"), )) + + @Test def methodsWithInstantiatedTypeVars2: Unit = + code"""|object Test: + | class TestSelect() + |object T: + | extension (x: Test.TestSel$m1) + |""" + .completion(m1, Set( + ("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"), + )) + + @Test def methodsWithInstantiatedTypeVars3: Unit = + code"""| + |class TestSelect() + |class Test(x: TestSel$m1) + |""" + .completion(m1, Set( + ("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"), + )) + + + + } diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionSuffix.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala similarity index 58% rename from presentation-compiler/src/main/dotty/tools/pc/completions/CompletionSuffix.scala rename to presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala index 580d65089737..806aa34c7f5e 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionSuffix.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala @@ -4,18 +4,19 @@ package dotty.tools.pc.completions * @param suffixes which we should insert * @param snippet which suffix should we insert the snippet $0 */ -case class CompletionSuffix( +case class CompletionAffix( suffixes: Set[SuffixKind], + prefix: PrefixKind, snippet: SuffixKind, ): def addLabelSnippet = suffixes.contains(SuffixKind.Bracket) def hasSnippet = snippet != SuffixKind.NoSuffix - def chain(copyFn: CompletionSuffix => CompletionSuffix) = copyFn(this) - def withNewSuffix(kind: SuffixKind) = - CompletionSuffix(suffixes + kind, snippet) + def chain(copyFn: CompletionAffix => CompletionAffix) = copyFn(this) + def withNewSuffix(kind: SuffixKind) = this.copy(suffixes = suffixes + kind) + def withNewPrefix(kind: PrefixKind) = this.copy(prefix = kind) def withNewSuffixSnippet(kind: SuffixKind) = - CompletionSuffix(suffixes + kind, kind) - def toEdit: String = + this.copy(suffixes = suffixes + kind, snippet = kind) + def toSuffix: String = def loop(suffixes: List[SuffixKind]): String = def cursor = if suffixes.head == snippet then "$0" else "" suffixes match @@ -24,16 +25,25 @@ case class CompletionSuffix( case SuffixKind.Template :: tail => s" {$cursor}" + loop(tail) case _ => "" loop(suffixes.toList) - def toEditOpt: Option[String] = - val edit = toEdit + def toSuffixOpt: Option[String] = + val edit = toSuffix if edit.nonEmpty then Some(edit) else None -end CompletionSuffix -object CompletionSuffix: - val empty = CompletionSuffix( + def toPrefix: String = prefix match + case PrefixKind.New => "new " + case PrefixKind.NoPrefix => "" + +end CompletionAffix + +object CompletionAffix: + val empty = CompletionAffix( suffixes = Set.empty, + prefix = PrefixKind.NoPrefix, snippet = SuffixKind.NoSuffix, ) enum SuffixKind: case Brace, Bracket, Template, NoSuffix + +enum PrefixKind: + case New, NoPrefix diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index 2beb4460db56..6ef9edd1db8b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -196,7 +196,8 @@ class CompletionProvider( item end mkItem - val completionTextSuffix = completion.snippetSuffix.toEdit + val completionTextSuffix = completion.snippetSuffix.toSuffix + val completionTextPrefix = completion.snippetSuffix.toPrefix lazy val isInStringInterpolation = path match @@ -232,7 +233,7 @@ class CompletionProvider( mkItem(nameEdit.getNewText().nn, other.toList, range = Some(nameEdit.getRange().nn)) case _ => mkItem( - v.insertText.getOrElse( ident.backticked(backtickSoftKeyword) + completionTextSuffix), + v.insertText.getOrElse(completionTextPrefix + ident.backticked(backtickSoftKeyword) + completionTextSuffix), edits.edits, range = v.range ) @@ -242,6 +243,7 @@ class CompletionProvider( case IndexedContext.Result.InScope => mkItem( v.insertText.getOrElse( + completionTextPrefix + ident.backticked( backtickSoftKeyword ) + completionTextSuffix @@ -250,17 +252,17 @@ class CompletionProvider( ) case _ if isInStringInterpolation => mkItem( - "{" + sym.fullNameBackticked + completionTextSuffix + "}", + "{" + completionTextPrefix + sym.fullNameBackticked + completionTextSuffix + "}", range = v.range ) case _ if v.isExtensionMethod => mkItem( - ident.backticked(backtickSoftKeyword) + completionTextSuffix, + completionTextPrefix + ident.backticked(backtickSoftKeyword) + completionTextSuffix, range = v.range ) case _ => mkItem( - sym.fullNameBackticked( + completionTextPrefix + sym.fullNameBackticked( backtickSoftKeyword ) + completionTextSuffix, range = v.range @@ -279,7 +281,7 @@ class CompletionProvider( val insert = completion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) mkItem( - insert + completionTextSuffix, + completionTextPrefix + insert + completionTextSuffix, range = completion.range ) end match 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 2810fe728b9a..806ba3abb7e6 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala @@ -40,7 +40,7 @@ enum CompletionSource: sealed trait CompletionValue: def label: String def insertText: Option[String] = None - def snippetSuffix: CompletionSuffix = CompletionSuffix.empty + def snippetSuffix: CompletionAffix = CompletionAffix.empty def additionalEdits: List[TextEdit] = Nil def range: Option[Range] = None def filterText: Option[String] = None @@ -66,7 +66,6 @@ object CompletionValue: sealed trait Symbolic extends CompletionValue: def denotation: Denotation val symbol = denotation.symbol - def isFromWorkspace: Boolean = false override def completionItemDataKind = CompletionItemData.None def isExtensionMethod: Boolean = false @@ -97,13 +96,11 @@ object CompletionValue: override def labelWithDescription( printer: ShortenedTypePrinter )(using Context): String = - if symbol.is(Method) then s"${label}${description(printer)}" - else if symbol.isConstructor then label + if symbol.isConstructor then snippetSuffix.toPrefix + label + else if symbol.is(Method) then s"${label}${description(printer)}" else if symbol.is(Mutable) then s"$label: ${description(printer)}" else if symbol.is(Package) || symbol.is(Module) || symbol.isClass then - if isFromWorkspace then - s"${labelWithSuffix(printer)} -${description(printer)}" - else s"${labelWithSuffix(printer)}${description(printer)}" + s"${labelWithSuffix(printer)}${description(printer)}" else if symbol.isType then labelWithSuffix(printer) else if symbol.isTerm && symbol.info.typeSymbol.is(Module) then s"${label}${description(printer)}" @@ -126,29 +123,30 @@ object CompletionValue: case class Compiler( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix + override val snippetSuffix: CompletionAffix ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.CompilerKind.ordinal case class Scope( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix, + override val snippetSuffix: CompletionAffix, ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.ScopeKind.ordinal case class Workspace( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix, + override val snippetSuffix: CompletionAffix, override val importSymbol: Symbol ) extends Symbolic: - override def isFromWorkspace: Boolean = true override def completionItemDataKind: Integer = CompletionSource.WorkspaceKind.ordinal override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = - if symbol.is(Method) && symbol.name != nme.apply then + if symbol.is(Method) && symbol.name != nme.apply && !symbol.isConstructor then s"${labelWithSuffix(printer)} - ${printer.fullNameString(symbol.effectiveOwner)}" + else if symbol.is(Package) || symbol.is(Module) || symbol.isClass then + s"${labelWithSuffix(printer)} -${description(printer)}" else super.labelWithDescription(printer) /** @@ -157,7 +155,7 @@ object CompletionValue: case class ImplicitClass( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix, + override val snippetSuffix: CompletionAffix, override val importSymbol: Symbol, ) extends Symbolic: override def completionItemKind(using Context): CompletionItemKind = @@ -172,7 +170,7 @@ object CompletionValue: case class Extension( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix + override val snippetSuffix: CompletionAffix ) extends Symbolic: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Method 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 81a543701817..69226a20f57b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -36,6 +36,17 @@ import dotty.tools.pc.utils.MtagsEnrichments.* import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.interactive.Interactive +import java.nio.file.Path +import java.nio.file.Paths +import scala.collection.mutable +import scala.meta.internal.metals.ReportContext +import scala.meta.internal.mtags.CoursierComplete +import scala.meta.internal.pc.CompletionFuzzy +import scala.meta.internal.pc.IdentifierComparator +import scala.meta.internal.pc.MemberOrdering +import scala.meta.pc.* +import scala.collection.concurrent.TrieMap + class Completions( text: String, ctx: Context, @@ -72,7 +83,7 @@ class Completions( case (_: Ident) :: (_: SeqLiteral) :: _ => false case _ => true - private lazy val allowTemplateSuffix: Boolean = + private lazy val isNew: Boolean = path match case _ :: New(selectOrIdent: (Select | Ident)) :: _ => true case _ => false @@ -105,14 +116,23 @@ class Completions( end if end includeSymbol + def enrichedCompilerCompletions(qualType: Type): (List[CompletionValue], SymbolSearch.Result) = + val compilerCompletions = Completion + .rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath) + + compilerCompletions + .toList + .flatMap(toCompletionValues) + .filterInteresting(qualType) + def completions(): (List[CompletionValue], SymbolSearch.Result) = val (advanced, exclusive) = advancedCompletions(path, completionPos) val (all, result) = if exclusive then (advanced, SymbolSearch.Result.COMPLETE) else - val keywords = - KeywordsCompletions.contribute(path, completionPos, comments) + val keywords = KeywordsCompletions.contribute(path, completionPos, comments) val allAdvanced = advanced ++ keywords + path match // should not show completions for toplevel case Nil | (_: PackageDef) :: _ if !completionPos.originalCursorPosition.source.file.ext.isScalaScript => @@ -121,17 +141,10 @@ class Completions( (allAdvanced, SymbolSearch.Result.COMPLETE) case Select(qual, _) :: _ => val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath) - val (compiler, result) = compilerCompletions - .toList - .flatMap(toCompletionValues) - .filterInteresting(qual.typeOpt.widenDealias) + val (compiler, result) = enrichedCompilerCompletions(qual.typeOpt.widenDealias) (allAdvanced ++ compiler, result) case _ => - val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath) - val (compiler, result) = compilerCompletions - .toList - .flatMap(toCompletionValues) - .filterInteresting() + val (compiler, result) = enrichedCompilerCompletions(defn.AnyType) (allAdvanced ++ compiler, result) end match @@ -184,16 +197,15 @@ class Completions( ) end isAbstractType - private def findSuffix(symbol: Symbol): CompletionSuffix = - CompletionSuffix.empty + private def findSuffix(symbol: Symbol): CompletionAffix = + CompletionAffix.empty .chain { suffix => // for [] suffix - if shouldAddSnippet && symbol.info.typeParams.nonEmpty - then suffix.withNewSuffixSnippet(SuffixKind.Bracket) + if shouldAddSnippet && symbol.info.typeParams.nonEmpty then + suffix.withNewSuffixSnippet(SuffixKind.Bracket) else suffix } .chain { suffix => // for () suffix - if shouldAddSnippet && symbol.is(Flags.Method) - then + if shouldAddSnippet && symbol.is(Flags.Method) then val paramss = getParams(symbol) paramss match case Nil => suffix @@ -215,9 +227,7 @@ class Completions( else suffix } .chain { suffix => // for {} suffix - if shouldAddSnippet && allowTemplateSuffix - && isAbstractType(symbol) - then + if shouldAddSnippet && isNew && isAbstractType(symbol) then if suffix.hasSnippet then suffix.withNewSuffix(SuffixKind.Template) else suffix.withNewSuffixSnippet(SuffixKind.Template) else suffix @@ -228,32 +238,46 @@ class Completions( def completionsWithSuffix( denot: SingleDenotation, label: String, - toCompletionValue: (String, SingleDenotation, CompletionSuffix) => CompletionValue + toCompletionValue: (String, SingleDenotation, CompletionAffix) => CompletionValue ): List[CompletionValue] = val sym = denot.symbol - // find the apply completion that would need a snippet val methodDenots: List[SingleDenotation] = - if shouldAddSnippet && completionMode.is(Mode.Term) && + if shouldAddSnippet && (completionMode.is(Mode.Term) || (isNew && !sym.isClass && !sym.isField)) && (sym.is(Flags.Module) || sym.isField || sym.isClass && !sym.is(Flags.Trait)) && !sym.is(Flags.JavaDefined) then - val info = - /* Companion will be added even for normal classes now, - * but it will not show up from classpath. We can suggest - * constructors based on those synthetic applies. - */ - if sym.isClass && sym.companionModule.exists then sym.companionModule.info - else denot.info - val applyDenots = info.member(nme.apply).allSymbols.map(_.asSeenFrom(info).asSingleDenotation) - denot :: applyDenots - else denot :: Nil + val constructors = if sym.isAllOf(ConstructorProxyModule) then + sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols + else + val companionApplies = denot.info.member(nme.apply).allSymbols + val classConstructors = if sym.companionClass.exists && !sym.companionClass.is(Abstract) then + sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols + else Nil + + if companionApplies.exists(_.is(Synthetic)) then + companionApplies ++ classConstructors.filter(!_.isPrimaryConstructor) + else + companionApplies ++ classConstructors + + val applyDenots = constructors.map(_.asSeenFrom(denot.info).asSingleDenotation) + .filter(_.symbol.isAccessibleFrom(denot.info)) + + if sym.isAllOf(ConstructorProxyModule) || sym.is(Trait) then applyDenots + else denot :: applyDenots + else + denot :: Nil + + + val disambiguiateInits = methodDenots.exists(_.symbol.isConstructor) && methodDenots.exists(_.symbol.name == nme.apply) methodDenots.map { methodDenot => + val prefix = if methodDenot.symbol.isConstructor && disambiguiateInits then PrefixKind.New else PrefixKind.NoPrefix val suffix = findSuffix(methodDenot.symbol) + val affix = suffix.withNewPrefix(prefix) val name = undoBacktick(label) toCompletionValue( name, methodDenot, - suffix + affix ) } end completionsWithSuffix @@ -569,13 +593,39 @@ class Completions( sym.showFullName + sigString else sym.fullName.stripModuleClassSuffix.show + /** If we try to complete TypeName, we should favor types over terms with same name value and without suffix. + */ + def deduplicateCompletions(completions: List[CompletionValue]): List[CompletionValue] = + val typeResultMappings = mutable.Map.empty[Name, Seq[CompletionValue]] + val (symbolicCompletions, rest) = completions.partition: + _.isInstanceOf[CompletionValue.Symbolic] + + val symbolicCompletionsMap = symbolicCompletions + .collect { case symbolic: CompletionValue.Symbolic => symbolic } + .groupBy(_.symbol.fullName) // we somehow have to ignore proxy type + + symbolicCompletionsMap.foreach: (name, denots) => + lazy val existsTypeWithSuffix: Boolean = symbolicCompletionsMap + .get(name.toTypeName) + .forall(_.forall(sym => sym.snippetSuffix.suffixes.nonEmpty)) + + if completionMode.is(Mode.Term) && !completionMode.is(Mode.ImportOrExport) then + typeResultMappings += name -> denots + // show non synthetic symbols + // companion test should not result TrieMap[K, V] + else if name.isTermName && existsTypeWithSuffix then + typeResultMappings += name -> denots + else if name.isTypeName then + typeResultMappings += name -> denots + + typeResultMappings.toList.unzip._2.flatten ++ rest + extension (l: List[CompletionValue]) def filterInteresting( qualType: Type = ctx.definitions.AnyType, enrich: Boolean = true ): (List[CompletionValue], SymbolSearch.Result) = - - val isSeen = mutable.Set.empty[String] + val alreadySeen = mutable.Set.empty[String] val buf = List.newBuilder[CompletionValue] def visit(head: CompletionValue): Boolean = val (id, include) = @@ -586,14 +636,9 @@ class Completions( case symOnly: CompletionValue.Symbolic => val sym = symOnly.symbol val name = SemanticdbSymbols.symbolName(sym) - val nameId = - if sym.isClass || sym.is(Module) then - // drop #|. at the end to avoid duplication - name.substring(0, name.length() - 1).nn - else name val suffix = if symOnly.snippetSuffix.addLabelSnippet then "[]" else "" - val id = nameId + suffix + val id = name + suffix val include = includeSymbol(sym) (id, include) case kw: CompletionValue.Keyword => (kw.label, true) @@ -604,8 +649,8 @@ class Completions( (fileSysMember.label, true) case ii: CompletionValue.IvyImport => (ii.label, true) - if !isSeen(id) && include then - isSeen += id + if !alreadySeen(id) && include then + alreadySeen += id buf += head true else false @@ -615,12 +660,9 @@ class Completions( if enrich then val searchResult = - enrichWithSymbolSearch(visit, qualType).getOrElse( - SymbolSearch.Result.COMPLETE - ) - (buf.result, searchResult) - else (buf.result, SymbolSearch.Result.COMPLETE) - + enrichWithSymbolSearch(visit, qualType).getOrElse(SymbolSearch.Result.COMPLETE) + (deduplicateCompletions(buf.result), searchResult) + else (deduplicateCompletions(buf.result), SymbolSearch.Result.COMPLETE) end filterInteresting end extension diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala index 2a8ead70ea33..50dd462bb028 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala @@ -162,7 +162,7 @@ object InterpolatorCompletions: CompletionValue.Interpolator( denot.symbol, label, - Some(newText(name, suffix.toEditOpt, identOrSelect)), + Some(newText(name, suffix.toSuffixOpt, identOrSelect)), Nil, Some(completionPos.originalCursorPosition.withStart(identOrSelect.span.start).toLsp), // Needed for VS Code which will not show the completion otherwise @@ -293,7 +293,7 @@ object InterpolatorCompletions: CompletionValue.Interpolator( denot.symbol, label, - Some(newText(name, suffix.toEditOpt)), + Some(newText(name, suffix.toSuffixOpt)), additionalEdits(), Some(nameRange), None, From 34f97be8765ef0d2b0aeb478ae2094050517b1ac Mon Sep 17 00:00:00 2001 From: rochala Date: Mon, 18 Mar 2024 14:48:04 +0100 Subject: [PATCH 02/10] Change how we add constructors, refactors --- .../tools/dotc/interactive/Completion.scala | 55 +- .../tools/languageserver/CompletionTest.scala | 52 +- .../pc/completions/CompletionAffix.scala | 21 +- .../pc/completions/CompletionProvider.scala | 13 +- .../pc/completions/CompletionValue.scala | 6 +- .../tools/pc/completions/Completions.scala | 114 ++-- .../tools/pc/base/BaseCompletionSuite.scala | 7 +- .../tests/completion/CompletionDocSuite.scala | 20 +- .../CompletionExtraConstructorSuite.scala | 514 ++++++++++++++++++ .../CompletionInterpolatorSuite.scala | 10 +- .../completion/CompletionKeywordSuite.scala | 9 +- .../completion/CompletionOverrideSuite.scala | 2 +- .../completion/CompletionSnippetSuite.scala | 39 +- .../pc/tests/completion/CompletionSuite.scala | 194 ++----- .../completion/CompletionWorkspaceSuite.scala | 14 +- 15 files changed, 754 insertions(+), 316 deletions(-) create mode 100644 presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index c51593f2a159..44407daf600c 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -125,7 +125,7 @@ object Completion: def completionPrefix(path: List[untpd.Tree], pos: SourcePosition)(using Context): String = def fallback: Int = var i = pos.point - 1 - while i >= 0 && Chars.isIdentifierPart(pos.source.content()(i)) do i -= 1 + while i >= 0 && Character.isUnicodeIdentifierPart(pos.source.content()(i)) do i -= 1 i + 1 path match @@ -273,6 +273,32 @@ object Completion: if denot.isType then denot.symbol.showFullName else denot.info.widenTermRefExpr.show + /** Include in completion sets only symbols that + * 1. is not absent (info is not NoType) + * 2. are not a primary constructor, + * 3. have an existing source symbol, + * 4. are the module class in case of packages, + * 5. are mutable accessors, to exclude setters for `var`, + * 6. symbol is not a package object + * 7. symbol is not an artifact of the compiler + * 8. symbol is not a constructor proxy module when in type completion mode + * 9. have same term/type kind as name prefix given so far + */ + def isValidCompletionSymbol(sym: Symbol, completionMode: Mode)(using Context): Boolean = + sym.exists && + !sym.isAbsent() && + !sym.isPrimaryConstructor && + sym.sourceSymbol.exists && + (!sym.is(Package) || sym.is(ModuleClass)) && + !sym.isAllOf(Mutable | Accessor) && + !sym.isPackageObject && + !sym.is(Artifact) && + !(completionMode.is(Mode.Type) && sym.isAllOf(ConstructorProxyModule)) && + ( + (completionMode.is(Mode.Term) && (sym.isTerm || sym.is(ModuleClass)) + || (completionMode.is(Mode.Type) && (sym.isType || sym.isStableMember))) + ) + given ScopeOrdering(using Context): Ordering[Seq[SingleDenotation]] with val order = List(defn.ScalaPredefModuleClass, defn.ScalaPackageClass, defn.JavaLangPackageClass) @@ -526,34 +552,13 @@ object Completion: extMethodsWithAppliedReceiver.groupByName /** Include in completion sets only symbols that - * 1. start with given name prefix, and - * 2. is not absent (info is not NoType) - * 3. are not a primary constructor, - * 4. have an existing source symbol, - * 5. are the module class in case of packages, - * 6. are mutable accessors, to exclude setters for `var`, - * 7. symbol is not a package object - * 8. symbol is not an artifact of the compiler - * 9. have same term/type kind as name prefix given so far + * 1. match the filter method, + * 2. satisfy [[Completion.isValidCompletionSymbol]] */ private def include(denot: SingleDenotation, nameInScope: Name)(using Context): Boolean = - val sym = denot.symbol - - nameInScope.startsWith(prefix) && - sym.exists && completionsFilter(NoType, nameInScope) && - !sym.isAbsent() && - !sym.isPrimaryConstructor && - sym.sourceSymbol.exists && - (!sym.is(Package) || sym.is(ModuleClass)) && - !sym.isAllOf(Mutable | Accessor) && - !sym.isPackageObject && - !sym.is(Artifact) && - ( - (mode.is(Mode.Term) && (sym.isTerm || sym.is(ModuleClass)) - || (mode.is(Mode.Type) && (sym.isType || sym.isStableMember))) - ) + isValidCompletionSymbol(denot.symbol, mode) private def extractRefinements(site: Type)(using Context): Seq[SingleDenotation] = site match diff --git a/language-server/test/dotty/tools/languageserver/CompletionTest.scala b/language-server/test/dotty/tools/languageserver/CompletionTest.scala index e770a2195dff..6ef8bee8a5d2 100644 --- a/language-server/test/dotty/tools/languageserver/CompletionTest.scala +++ b/language-server/test/dotty/tools/languageserver/CompletionTest.scala @@ -954,14 +954,8 @@ class CompletionTest { .noCompletions() @Test def i13624_annotType: Unit = - val expected1 = Set( - ("MyAnnotation", Class, "MyAnnotation"), - ("MyAnnotation", Module, "MyAnnotation"), - ) - val expected2 = Set( - ("MyAnnotation", Class, "Foo.MyAnnotation"), - ("MyAnnotation", Module, "Foo.MyAnnotation"), - ) + val expected1 = Set(("MyAnnotation", Class, "MyAnnotation")) + val expected2 = Set(("MyAnnotation", Class, "Foo.MyAnnotation")) code"""object Foo{ | class MyAnnotation extends annotation.StaticAnnotation |} @@ -984,14 +978,8 @@ class CompletionTest { @Test def i13624_annotation : Unit = code"""@annotation.implicitNot${m1} |@annotation.implicitNotFound @mai${m2}""" - .completion(m1, - ("implicitNotFound", Class, "scala.annotation.implicitNotFound"), - ("implicitNotFound", Module, "scala.annotation.implicitNotFound"), - ) - .completion(m2, - ("main", Class, "main"), - ("main", Module, "main"), - ) + .completion(m1, ("implicitNotFound", Class, "scala.annotation.implicitNotFound")) + .completion(m2, ("main", Class, "main")) @Test def i13623_annotation : Unit = code"""import annot${m1}""" @@ -1489,7 +1477,6 @@ class CompletionTest { ("xDef", Method, "=> Int"), ("xVal", Field, "Int"), ("xObject", Module, "Foo.xObject"), - ("xClass", Module, "Foo.xClass"), ("xClass", Class, "Foo.xClass"))) } @@ -1557,9 +1544,7 @@ class CompletionTest { |object T: | extension (x: Test.TestSel$m1) |""" - .completion(m1, Set( - ("TestSelect", Module, "Test.TestSelect"), ("TestSelect", Class, "Test.TestSelect") - )) + .completion(m1, Set(("TestSelect", Class, "Test.TestSelect"))) @Test def extensionDefinitionCompletionsSelectNested: Unit = code"""|object Test: @@ -1568,9 +1553,7 @@ class CompletionTest { |object T: | extension (x: Test.Test2.TestSel$m1) |""" - .completion(m1, Set( - ("TestSelect", Module, "Test.Test2.TestSelect"), ("TestSelect", Class, "Test.Test2.TestSelect") - )) + .completion(m1, Set(("TestSelect", Class, "Test.Test2.TestSelect"))) @Test def extensionDefinitionCompletionsSelectInside: Unit = code"""|object Test: @@ -1721,27 +1704,4 @@ class CompletionTest { .completion(m1, Set( ("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"), )) - - @Test def methodsWithInstantiatedTypeVars2: Unit = - code"""|object Test: - | class TestSelect() - |object T: - | extension (x: Test.TestSel$m1) - |""" - .completion(m1, Set( - ("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"), - )) - - @Test def methodsWithInstantiatedTypeVars3: Unit = - code"""| - |class TestSelect() - |class Test(x: TestSel$m1) - |""" - .completion(m1, Set( - ("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"), - )) - - - - } diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala index 806aa34c7f5e..456f5cf5b12b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala @@ -6,16 +6,19 @@ package dotty.tools.pc.completions */ case class CompletionAffix( suffixes: Set[SuffixKind], - prefix: PrefixKind, + prefixes: List[PrefixKind], snippet: SuffixKind, ): def addLabelSnippet = suffixes.contains(SuffixKind.Bracket) def hasSnippet = snippet != SuffixKind.NoSuffix def chain(copyFn: CompletionAffix => CompletionAffix) = copyFn(this) def withNewSuffix(kind: SuffixKind) = this.copy(suffixes = suffixes + kind) - def withNewPrefix(kind: PrefixKind) = this.copy(prefix = kind) + def withNewPrefix(kind: PrefixKind) = this.copy(prefixes = prefixes :+ kind) def withNewSuffixSnippet(kind: SuffixKind) = this.copy(suffixes = suffixes + kind, snippet = kind) + + def nonEmpty: Boolean = suffixes.nonEmpty || prefixes.nonEmpty + def toSuffix: String = def loop(suffixes: List[SuffixKind]): String = def cursor = if suffixes.head == snippet then "$0" else "" @@ -25,20 +28,24 @@ case class CompletionAffix( case SuffixKind.Template :: tail => s" {$cursor}" + loop(tail) case _ => "" loop(suffixes.toList) + def toSuffixOpt: Option[String] = val edit = toSuffix if edit.nonEmpty then Some(edit) else None - def toPrefix: String = prefix match - case PrefixKind.New => "new " - case PrefixKind.NoPrefix => "" + def toPrefix: String = + def loop(prefixes: List[PrefixKind]) = + prefixes match + case PrefixKind.New :: tail => "new " + case _ => "" + loop(prefixes) end CompletionAffix object CompletionAffix: val empty = CompletionAffix( suffixes = Set.empty, - prefix = PrefixKind.NoPrefix, + prefixes = Nil, snippet = SuffixKind.NoSuffix, ) @@ -46,4 +53,4 @@ enum SuffixKind: case Brace, Bracket, Template, NoSuffix enum PrefixKind: - case New, NoPrefix + case New diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index 6ef9edd1db8b..fb57a7cdabc5 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -278,12 +278,13 @@ class CompletionProvider( case v: CompletionValue.Interpolator if v.isWorkspace || v.isExtension => mkItemWithImports(v) case _ => - val insert = - completion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) - mkItem( - completionTextPrefix + insert + completionTextSuffix, - range = completion.range - ) + val nameText = completion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) + val nameWithAffixes = completionTextPrefix + nameText + completionTextSuffix + val insertText = if completion.snippetSuffix.nonEmpty && isInStringInterpolation then + "{" + nameWithAffixes + "}" + else nameWithAffixes + mkItem(insertText, range = completion.range) + end match end completionItems end CompletionProvider 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 806ba3abb7e6..d623d1f434f1 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala @@ -96,7 +96,7 @@ object CompletionValue: override def labelWithDescription( printer: ShortenedTypePrinter )(using Context): String = - if symbol.isConstructor then snippetSuffix.toPrefix + label + if symbol.isConstructor then s"${snippetSuffix.toPrefix}${label}${description(printer)}" else if symbol.is(Method) then s"${label}${description(printer)}" else if symbol.is(Mutable) then s"$label: ${description(printer)}" else if symbol.is(Package) || symbol.is(Module) || symbol.isClass then @@ -143,7 +143,9 @@ object CompletionValue: override def completionItemDataKind: Integer = CompletionSource.WorkspaceKind.ordinal override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = - if symbol.is(Method) && symbol.name != nme.apply && !symbol.isConstructor then + if symbol.isConstructor || symbol.name == nme.apply then + s"${snippetSuffix.toPrefix}${label}${description(printer)} - ${printer.fullNameString(importSymbol.effectiveOwner)}" + else if symbol.is(Method) then s"${labelWithSuffix(printer)} - ${printer.fullNameString(symbol.effectiveOwner)}" else if symbol.is(Package) || symbol.is(Module) || symbol.isClass then s"${labelWithSuffix(printer)} -${description(printer)}" 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 69226a20f57b..ef81416480b4 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -80,7 +80,6 @@ class Completions( false case (_: (Import | Export)) :: _ => false case _ :: (_: (Import | Export)) :: _ => false - case (_: Ident) :: (_: SeqLiteral) :: _ => false case _ => true private lazy val isNew: Boolean = @@ -99,7 +98,6 @@ class Completions( val generalExclude = isUninterestingSymbol(sym) || !isNotLocalForwardReference(sym) || - sym.isPackageObject || hasSyntheticCursorSuffix def isWildcardParam(sym: Symbol) = @@ -170,13 +168,17 @@ class Completions( inline private def undoBacktick(label: String): String = label.stripPrefix("`").stripSuffix("`") + // TODO This has to be refactored to properly split extension methods + // This method has to be fixed even further. The similar problem will be present in shortened type printer. private def getParams(symbol: Symbol) = lazy val extensionParam = symbol.extensionParam if symbol.is(Flags.Extension) then symbol.paramSymss.filterNot( _.contains(extensionParam) ) - else symbol.paramSymss + else if symbol.isConstructor then + symbol.owner.paramSymss + else symbol.paramSymss.filter(!_.exists(_.isTypeParam)) private def isAbstractType(symbol: Symbol) = (symbol.info.typeSymbol.is(Trait) // trait A{ def doSomething: Int} @@ -241,15 +243,19 @@ class Completions( toCompletionValue: (String, SingleDenotation, CompletionAffix) => CompletionValue ): List[CompletionValue] = val sym = denot.symbol + val hasNonSyntheticConstructor = sym.name.isTypeName && sym.isClass + && !sym.is(ModuleClass) && !sym.is(Trait) && !sym.is(Abstract) && !sym.is(Flags.JavaDefined) + val methodDenots: List[SingleDenotation] = - if shouldAddSnippet && (completionMode.is(Mode.Term) || (isNew && !sym.isClass && !sym.isField)) && - (sym.is(Flags.Module) || sym.isField || sym.isClass && !sym.is(Flags.Trait)) && !sym.is(Flags.JavaDefined) - then + if shouldAddSnippet && isNew && hasNonSyntheticConstructor then + sym.info.member(nme.CONSTRUCTOR).allSymbols.map(_.asSingleDenotation) + .filter(_.symbol.isAccessibleFrom(denot.info)) + else if shouldAddSnippet && completionMode.is(Mode.Term) && sym.name.isTermName && !sym.is(Flags.Method) && !sym.is(Flags.JavaDefined) then val constructors = if sym.isAllOf(ConstructorProxyModule) then sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols else val companionApplies = denot.info.member(nme.apply).allSymbols - val classConstructors = if sym.companionClass.exists && !sym.companionClass.is(Abstract) then + val classConstructors = if sym.companionClass.exists then sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols else Nil @@ -258,27 +264,23 @@ class Completions( else companionApplies ++ classConstructors - val applyDenots = constructors.map(_.asSeenFrom(denot.info).asSingleDenotation) + val extraDenots = constructors.map(_.asSeenFrom(denot.info).asSingleDenotation) .filter(_.symbol.isAccessibleFrom(denot.info)) - if sym.isAllOf(ConstructorProxyModule) || sym.is(Trait) then applyDenots - else denot :: applyDenots - else - denot :: Nil + if sym.isAllOf(ConstructorProxyModule) || sym.is(Trait) then extraDenots + else denot :: extraDenots + else denot :: Nil - val disambiguiateInits = methodDenots.exists(_.symbol.isConstructor) && methodDenots.exists(_.symbol.name == nme.apply) + val requiresInitDisambiguiation = methodDenots.exists(_.symbol.isConstructor) && methodDenots.exists(_.symbol.name == nme.apply) methodDenots.map { methodDenot => - val prefix = if methodDenot.symbol.isConstructor && disambiguiateInits then PrefixKind.New else PrefixKind.NoPrefix val suffix = findSuffix(methodDenot.symbol) - val affix = suffix.withNewPrefix(prefix) + val affix = if methodDenot.symbol.isConstructor && requiresInitDisambiguiation then + suffix.withNewPrefix(PrefixKind.New) + else suffix val name = undoBacktick(label) - toCompletionValue( - name, - methodDenot, - affix - ) + toCompletionValue(name, methodDenot, affix) } end completionsWithSuffix @@ -519,11 +521,20 @@ class Completions( val query = completionPos.query if completionMode.is(Mode.Scope) && query.nonEmpty then val visitor = new CompilerSearchVisitor(sym => - if !(sym.is(Flags.ExtensionMethod) || - (sym.maybeOwner.is(Flags.Implicit) && sym.maybeOwner.isClass)) + if Completion.isValidCompletionSymbol(sym, completionMode) && + !(sym.is(Flags.ExtensionMethod) || (sym.maybeOwner.is(Flags.Implicit) && sym.maybeOwner.isClass)) then indexedContext.lookupSym(sym) match case IndexedContext.Result.InScope => false + case _ if completionMode.is(Mode.ImportOrExport) => + visit( + CompletionValue.Workspace( + label = undoBacktick(sym.decodedName), + denotation = sym, + snippetSuffix = CompletionAffix.empty, + importSymbol = sym + ) + ) case _ => completionsWithSuffix( sym, @@ -904,6 +915,16 @@ class Completions( prioritizeCaseKeyword || prioritizeNamed end compareCompletionValue + def methodScore(v: CompletionValue.Symbolic)(using Context): Int = + val sym = v.symbol + val workspacePenalty = if v.isInstanceOf[CompletionValue.Workspace] then 3 else 0 + val methodPenalty = + if isNew && sym.isConstructor then -1 + else if !completionMode.is(Mode.Member) && sym.name == nme.apply then 1 + else if sym.isConstructor then 2 + else 0 + workspacePenalty + methodPenalty + override def compare(o1: CompletionValue, o2: CompletionValue): Int = (o1, o2) match case (o1: CompletionValue.NamedArg, o2: CompletionValue.NamedArg) => @@ -923,32 +944,39 @@ class Completions( val byLocalSymbol = compareLocalSymbols(s1, s2) if byLocalSymbol != 0 then byLocalSymbol else - val byRelevance = compareByRelevance(o1, o2) - if byRelevance != 0 then byRelevance + val byFuzzy = Integer.compare( + fuzzyScore(sym1), + fuzzyScore(sym2) + ) + if byFuzzy != 0 then byFuzzy else - val byFuzzy = Integer.compare( - fuzzyScore(sym1), - fuzzyScore(sym2) + val byMethodScore = Integer.compare( + methodScore(sym1), + methodScore(sym2) ) - if byFuzzy != 0 then byFuzzy + if byMethodScore != 0 then byMethodScore else - val byIdentifier = IdentifierComparator.compare( - s1.name.show, - s2.name.show - ) - if byIdentifier != 0 then byIdentifier + val byRelevance = compareByRelevance(o1, o2) + if byRelevance != 0 then byRelevance else - val byOwner = - s1.owner.fullName.toString - .compareTo(s2.owner.fullName.toString) - if byOwner != 0 then byOwner + val byIdentifier = IdentifierComparator.compare( + s1.name.show, + s2.name.show + ) + if byIdentifier != 0 then byIdentifier else - val byParamCount = Integer.compare( - s1.paramSymss.flatten.size, - s2.paramSymss.flatten.size - ) - if byParamCount != 0 then byParamCount - else s1.detailString.compareTo(s2.detailString) + val byOwner = + s1.owner.fullName.toString + .compareTo(s2.owner.fullName.toString) + if byOwner != 0 then byOwner + else + val byParamCount = Integer.compare( + s1.paramSymss.flatten.size, + s2.paramSymss.flatten.size + ) + if byParamCount != 0 then byParamCount + else s1.detailString.compareTo(s2.detailString) + end if end if end if end if diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala index 964f6a6894a2..bfb31906bce1 100644 --- a/presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala @@ -207,7 +207,8 @@ abstract class BaseCompletionSuite extends BasePCSuite: includeDetail: Boolean = true, filename: String = "A.scala", filter: String => Boolean = _ => true, - enablePackageWrap: Boolean = true + enablePackageWrap: Boolean = true, + includeCompletionKind: Boolean = false, ): Unit = val out = new StringBuilder() val withPkg = @@ -221,13 +222,14 @@ abstract class BaseCompletionSuite extends BasePCSuite: filteredItems.foreach { item => val label = TestCompletions.getFullyQualifiedLabel(item) val commitCharacter = - if (includeCommitCharacter) + if includeCommitCharacter then Option(item.getCommitCharacters) .getOrElse(Collections.emptyList()) .asScala .mkString(" (commit: '", " ", "')") else "" val documentation = doc(item.getDocumentation) + val completionKind = Option.when(includeCompletionKind)(s" (${item.getKind.toString})").getOrElse("") if (includeDocs && documentation.nonEmpty) { out.append("> ").append(documentation).append("\n") } @@ -244,6 +246,7 @@ abstract class BaseCompletionSuite extends BasePCSuite: "" }) .append(commitCharacter) + .append(completionKind) .append("\n") } val completionSources = filteredItems diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala index 45f07b5fb7b1..973b780f0ccb 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala @@ -186,6 +186,7 @@ class CompletionDocSuite extends BaseCompletionSuite: |Try scala.util |> Found documentation for scala/util/Try.apply(). |Try[T](r: => T): Try[T] + |new Try[T]: Try[T] |""".stripMargin, includeDocs = true ) @@ -228,11 +229,8 @@ class CompletionDocSuite extends BaseCompletionSuite: |} """.stripMargin, """ - |> ### class Catch - |Found documentation for scala/util/control/Exception.Catch# - |### object Catch - |Found documentation for scala/util/control/Exception.Catch. - |Catch[T] - scala.util.control.Exception + |> Found documentation for scala/util/control/Exception.Catch# + |Catch[T](pf: Catcher[T], fin: Option[Finally] = ..., rethrow: Throwable => Boolean = ...): Catch[T] - scala.util.control.Exception |> ### class Catch |Found documentation for scala/util/control/Exception.Catch# |### object Catch @@ -264,16 +262,8 @@ class CompletionDocSuite extends BaseCompletionSuite: |} """.stripMargin, """ - |> ### class DynamicVariable - |Found documentation for scala/util/DynamicVariable# - |### object DynamicVariable - |Found documentation for scala/util/DynamicVariable. - |DynamicVariable[T] scala.util - |> ### class DynamicVariable - |Found documentation for scala/util/DynamicVariable# - |### object DynamicVariable - |Found documentation for scala/util/DynamicVariable. - |DynamicVariable scala.util + |> Found documentation for scala/util/DynamicVariable# + |DynamicVariable[T](init: T): DynamicVariable[T] |""".stripMargin, includeDocs = true ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala new file mode 100644 index 000000000000..963b563307b1 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala @@ -0,0 +1,514 @@ +package dotty.tools.pc.tests.completion + +import scala.meta.pc.SymbolDocumentation +import scala.language.unsafeNulls + +import dotty.tools.pc.base.BaseCompletionSuite +import dotty.tools.pc.utils.MockEntries + +import org.junit.Test +import org.junit.Ignore + +class CompletionExtraConstructorSuite extends BaseCompletionSuite: + + @Test def `no-extra-new-completions-class-1` = + check( + """|object Wrapper: + | class TestClass(x: Int) + | TestCla@@ + |""".stripMargin, + """|TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-new-completions-class-2` = + check( + """|object Wrapper: + | class TestClass() + | TestCla@@ + |""".stripMargin, + """|TestClass(): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-new-completions-class-3` = + check( + """|object Wrapper: + | class TestClass[T](x: T) + | TestCla@@ + |""".stripMargin, + """|TestClass[T](x: T): TestClass[T] (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-new-completions-case-class-1` = + check( + """|object Wrapper: + | case class TestClass(x: Int) + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int): TestClass (Method) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-new-completions-case-class-2` = + check( + """|object Wrapper: + | case class TestClass() + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(): TestClass (Method) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-new-completions-case-class-3` = + check( + """|object Wrapper: + | case class TestClass[T](x: T) + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass[T](x: T): TestClass[T] (Method) + |""".stripMargin, + includeCompletionKind = true + ) + + @Ignore + @Test def `extra-new-completions-abstract-class-1` = + check( + """|object Wrapper: + | abstract class TestClass(x: Int) + | TestCla@@ + |""".stripMargin, + """|new TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Ignore + @Test def `extra-new-completions-abstract-class-2` = + check( + """|object Wrapper: + | abstract class TestClass() + | TestCla@@ + |""".stripMargin, + """|new TestClass(): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Ignore + @Test def `extra-new-completions-abstract-class-3` = + check( + """|object Wrapper: + | abstract class TestClass[T](x: T) + | TestCla@@ + |""".stripMargin, + """|new TestClass[T](x: T): TestClass[T] (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Ignore + @Test def `extra-new-completions-trait-1` = + check( + """|object Wrapper: + | trait TestClass + | TestCla@@ + |""".stripMargin, + """|new TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-class-1` = + check( + """|object Wrapper: + | class TestClass(x: Int) + | object TestClass: + | def apply(x: Int, y: Int): TestClass = TestClass(x + y) + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int, y: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-class-2` = + check( + """|object Wrapper: + | class TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = TestClass(x) + | TestCla@@ + |} + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-class-3` = + check( + """|object Wrapper: + | class TestClass() + | object TestClass: + | def apply(): TestClass = TestClass(1) + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-abstract-class-with-companion-1` = + check( + """|object Wrapper: + | abstract class TestClass(x: Int) + | object TestClass: + | def apply(x: Int, y: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int, y: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-abstract-class-with-companion-2` = + check( + """|object Wrapper: + | abstract class TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-abstract-class-with-companion-3` = + check( + """|object Wrapper: + | abstract class TestClass() + | object TestClass: + | def apply(): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-trait-with-companion-1` = + check( + """|object Wrapper: + | trait TestClass(x: Int) + | object TestClass: + | def apply(x: Int, y: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int, y: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-trait-with-companion-2` = + check( + """|object Wrapper: + | trait TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `extra-new-completions-trait-with-companion-3` = + check( + """|object Wrapper: + | trait TestClass() + | object TestClass: + | def apply(): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + // This test should have new TestClass completion without parentheses. The actual issue is with printer, edit text is correct + @Test def `extra-new-completions-trait-with-companion-4` = + check( + """|object Wrapper: + | trait TestClass + | object TestClass: + | def apply(): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + checkSnippet( + """|object Wrapper: + | trait TestClass + | object TestClass: + | def apply(): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass + |TestClass() + |new TestClass + |""".stripMargin, + ) + + @Test def `multiple-extra-new-constructors-class-1` = + check( + """|object Wrapper: + | class TestClass(): + | def this(x: Int) = this() + | TestCla@@ + |""".stripMargin, + """|TestClass(): TestClass (Constructor) + |TestClass(x: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `multiple-extra-new-constructors-class-2` = + check( + """|object Wrapper: + | class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | TestCla@@ + |""".stripMargin, + """|TestClass(): TestClass (Constructor) + |TestClass(x: Int): TestClass (Constructor) + |TestClass(x: Int, y: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `multiple-extra-new-constructors-with-companion-2` = + check( + """|object Wrapper: + | class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(z: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(z: Int): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |new TestClass(x: Int): TestClass (Constructor) + |new TestClass(x: Int, y: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `multiple-extra-new-constructors-with-companion-3` = + check( + """|object Wrapper: + | class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(z: Int): TestClass = ??? + | def apply(z: Int, w: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(z: Int): TestClass (Method) + |TestClass(z: Int, w: Int): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |new TestClass(x: Int): TestClass (Constructor) + |new TestClass(x: Int, y: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `multiple-extra-new-constructors-with-companion-same-signature-class` = + check( + """|object Wrapper: + | class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |new TestClass(x: Int): TestClass (Constructor) + |new TestClass(x: Int, y: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `multiple-extra-new-constructors-with-companion-same-signature-case-class` = + check( + """|object Wrapper: + | case class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int): TestClass (Method) + |TestClass(): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |new TestClass(x: Int, y: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `multiple-extra-new-constructors-with-companion-same-signature-trait` = + check( + """|object Wrapper: + | trait TestClass: + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |new TestClass(x: Int): TestClass (Constructor) + |new TestClass(x: Int, y: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `multiple-extra-new-constructors-with-companion-same-signature-abstract` = + check( + """|object Wrapper: + | abstract class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Module) + |TestClass(x: Int): TestClass (Method) + |new TestClass(): TestClass (Constructor) + |new TestClass(x: Int): TestClass (Constructor) + |new TestClass(x: Int, y: Int): TestClass (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-completions-in-type-mode-1` = + check( + """|object Wrapper: + | class TestClass() + | val x: TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Class) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-completions-in-type-mode-2` = + check( + """|object Wrapper: + | class TestClass() + | val x: TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Class) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-completions-in-type-mode-3` = + check( + """|object Wrapper: + | class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(x: Int): TestClass = ??? + | val x: TestCla@@ + |""".stripMargin, + """|TestClass test.Wrapper (Class) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `workspace-no-extra-completions-in-type-mode-4` = + check( + """|object Wrapper: + | class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(x: Int): TestClass = ??? + |object M { + | val x: TestCla@@ + |} + |""".stripMargin, + """|TestClass - test.Wrapper (Class) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `workspace-multiple-extra-new-constructors` = + check( + """|object Wrapper: + | class TestClass(): + | def this(x: Int) = this() + | def this(x: Int, y: Int) = this() + | object TestClass: + | def apply(x: Int): TestClass = ??? + |object M { + | TestCla@@ + |} + |""".stripMargin, + """| + |TestClass - test.Wrapper (Module) + |TestClass(x: Int): TestClass - test.Wrapper (Method) + |new TestClass(): TestClass - test.Wrapper (Constructor) + |new TestClass(x: Int): TestClass - test.Wrapper (Constructor) + |new TestClass(x: Int, y: Int): TestClass - test.Wrapper (Constructor) + |""".stripMargin, + includeCompletionKind = true + ) + diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala index d9dc635ce21a..95997718a4d7 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala @@ -542,7 +542,7 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: |} |""".stripMargin, """s"Hello $hello@@"""".stripMargin, - """s"Hello $helloMethod"""".stripMargin, + """s"Hello ${helloMethod($0)}"""".stripMargin, filter = _.contains("a: Int") ) @@ -626,11 +626,11 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: | s"this is an interesting ${java.nio.file.Paths}" |} |""".stripMargin, + itemIndex = 0, assertSingleItem = false, - // Scala 3 has an additional Paths() completion - itemIndex = 2 ) + @Test def `auto-imports-prefix-with-interpolator` = checkEdit( """| @@ -644,7 +644,6 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: | s"this is an interesting ${java.nio.file.Paths}" |} |""".stripMargin, - // Scala 3 has an additional Paths object completion itemIndex = 1, assertSingleItem = false ) @@ -745,7 +744,8 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: |object Main { | val a = s"${ListBuffer($0)}"" |}""".stripMargin, - filter = _.contains("[A]") + assertSingleItem = false, + itemIndex = 1 ) @Test def `dont-show-when-writing-before-dollar` = diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala index 15c449904074..cc6751454d4f 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala @@ -151,8 +151,7 @@ class CompletionKeywordSuite extends BaseCompletionSuite: """|value: Int |val |var - |varargs(): varargs - |varargs - scala.annotation + |varargs(): varargs - scala.annotation |""".stripMargin ) @@ -169,8 +168,7 @@ class CompletionKeywordSuite extends BaseCompletionSuite: |""".stripMargin, """|val |var - |varargs(): varargs - |varargs - scala.annotation + |varargs(): varargs - scala.annotation |""".stripMargin ) @@ -203,8 +201,7 @@ class CompletionKeywordSuite extends BaseCompletionSuite: |} |""".stripMargin, """|value: Int - |varargs(): varargs - |varargs - scala.annotation""".stripMargin + |varargs(): varargs - scala.annotation""".stripMargin ) @Test def `val-trailing-space` = diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionOverrideSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionOverrideSuite.scala index 8bc45d344244..b3abc1474375 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionOverrideSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionOverrideSuite.scala @@ -926,8 +926,8 @@ class CompletionOverrideSuite extends BaseCompletionSuite: |} |""".stripMargin, """|def hello1: Int - |override val hello2: Int |override def equals(x$0: Any): Boolean + |override def hashCode(): Int |""".stripMargin, includeDetail = false, topLines = Some(3) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala index c3e3f374c23d..e731d95b8987 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala @@ -172,7 +172,6 @@ class CompletionSnippetSuite extends BaseCompletionSuite: |ArrayDequeOps[$0] |ArrayDeque |ArrayDeque - |ArrayDequeOps |""".stripMargin ) @@ -311,6 +310,7 @@ class CompletionSnippetSuite extends BaseCompletionSuite: |""".stripMargin, """|Try |Try($0) + |new Try |""".stripMargin ) @@ -322,9 +322,11 @@ class CompletionSnippetSuite extends BaseCompletionSuite: |""".stripMargin, // Note: the class and trait items in here are invalid. So // they are filtered out. - """|Try - |Try($0) - |""".stripMargin + """|Try - scala.util + |Try($0) - [T](r: => T): Try[T] + |new Try - [T]: Try[T] + |""".stripMargin, + includeDetail = true ) @Test def `symbol` = @@ -365,18 +367,33 @@ class CompletionSnippetSuite extends BaseCompletionSuite: checkSnippet( s"""|package example | - |object Widget{} + |object TestObject {} |object Main { - | Wi@@ + | TestObjec@@ |} |""".stripMargin, - """|Widget - example - |Window - java.awt - |WindowPeer - java.awt.peer - |WithFilter - scala.collection + """|TestObject - example + |""".stripMargin, + includeDetail = true, + ) + + @Test def `dont-enter-empty-paramlist` = + checkSnippet( + s"""|package example + | + |object Main { + | ListMa@@ + |} + |""".stripMargin, + """|ListMap - scala.collection.immutable + |ListMap - scala.collection.mutable + |ListMap($0) - [K, V](elems: (K, V)*): ListMap[K, V] + |new ListMap - [K, V]: ListMap[K, V] + |ListMapBuilder - [K, V]: ListMapBuilder[K, V] + |new ListMap - [K, V]: ListMap[K, V] + |ConcurrentSkipListMap - java.util.concurrent |""".stripMargin, includeDetail = true, - topLines = Some(4) ) // https://github.com/scalameta/metals/issues/4004 diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index eadadd484089..3ccf26643bb9 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -30,9 +30,8 @@ class CompletionSuite extends BaseCompletionSuite: |List[A](elems: A*): List[A] |List - java.awt |List - java.util - |ListMap[K, V](elems: (K, V)*): ListMap[K, V] |""".stripMargin, - topLines = Some(5) + topLines = Some(4) ) @Test def member = @@ -181,6 +180,22 @@ class CompletionSuite extends BaseCompletionSuite: |}""".stripMargin, """|TrieMap scala.collection.concurrent |TrieMap[K, V](elems: (K, V)*): TrieMap[K, V] + |new TrieMap[K, V]: TrieMap[K, V] + |new TrieMap[K, V](hashf: Hashing[K], ef: Equiv[K]): TrieMap[K, V] + |""".stripMargin + ) + + @Test def `no-companion-apply-in-new`= + check( + """ + |import scala.collection.concurrent._ + |object A { + | new TrieMap@@ + |}""".stripMargin, + // TrieMap should be filtered if it doesn't contain any types that can be constructed in `new` keyword context. + """|TrieMap[K, V]: TrieMap[K, V] + |TrieMap[K, V](hashf: Hashing[K], ef: Equiv[K]): TrieMap[K, V] + |TrieMap scala.collection.concurrent |""".stripMargin ) @@ -216,16 +231,13 @@ class CompletionSuite extends BaseCompletionSuite: """ |import JavaCon@@ |""".stripMargin, - """|AsJavaConverters - scala.collection.convert - |JavaConverters - scala.collection + """|JavaConverters - scala.collection |JavaConversions - scala.concurrent |AsJavaConsumer - scala.jdk.FunctionWrappers + |AsJavaConverters - scala.collection.convert |FromJavaConsumer - scala.jdk.FunctionWrappers |AsJavaBiConsumer - scala.jdk.FunctionWrappers |AsJavaIntConsumer - scala.jdk.FunctionWrappers - |AsJavaLongConsumer - scala.jdk.FunctionWrappers - |FromJavaBiConsumer - scala.jdk.FunctionWrappers - |FromJavaIntConsumer - scala.jdk.FunctionWrappers |""".stripMargin ) @@ -473,8 +485,7 @@ class CompletionSuite extends BaseCompletionSuite: | |} """.stripMargin, - """|DelayedLazyVal scala.concurrent - |DelayedLazyVal[T](f: () => T, body: => Unit)(exec: ExecutionContext): DelayedLazyVal[T]""".stripMargin + "DelayedLazyVal[T](f: () => T, body: => Unit)(implicit exec: ExecutionContext): DelayedLazyVal[T]" ) @Test def local2 = @@ -1154,8 +1165,7 @@ class CompletionSuite extends BaseCompletionSuite: |def main = | Testin@@ |""".stripMargin, - """|Testing a - |Testing(): Testing + """|Testing(): Testing |""".stripMargin ) @@ -1168,8 +1178,7 @@ class CompletionSuite extends BaseCompletionSuite: |def main = | Testin@@ |""".stripMargin, - """|Testing a - |Testing(a: Int, b: String): Testing + """|Testing(a: Int, b: String): Testing |""".stripMargin ) @@ -1314,28 +1323,28 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, """|AClass[A <: Int] test.O |AClass test.O - |AbstractTypeClassManifest - scala.reflect.ClassManifestFactory """.stripMargin ) + val extensionResult = + """|Foo test + |Found - scala.collection.Searching + |Font - java.awt + |Form - java.text.Normalizer + |Format - java.text + |FontPeer - java.awt.peer + |FormView - javax.swing.text.html + |Formatter - java.util + |Formatter - java.util.logging + |FocusEvent - java.awt.event""".stripMargin + @Test def `extension-definition-scope` = check( """|trait Foo |object T: | extension (x: Fo@@) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-symbol-search` = @@ -1354,18 +1363,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A <: Fo@@] |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-type-parameter-symbol-search` = @@ -1384,18 +1382,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (using Fo@@) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @@ -1405,18 +1392,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (x: Int)(using Fo@@) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-mix-2` = @@ -1425,18 +1401,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (using Fo@@)(x: Int)(using Foo) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-mix-3` = @@ -1445,18 +1410,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (using Foo)(x: Int)(using Fo@@) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-mix-4` = @@ -1465,18 +1419,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A](x: Fo@@) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-mix-5` = @@ -1485,18 +1428,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A](using Fo@@)(x: Int) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-mix-6` = @@ -1505,18 +1437,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A](using Foo)(x: Fo@@) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-mix-7` = @@ -1525,18 +1446,7 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A](using Foo)(x: Fo@@)(using Foo) |""".stripMargin, - """|Foo test - |Font - java.awt - |Form - java.text.Normalizer - |Format - java.text - |FontPeer - java.awt.peer - |FormView - javax.swing.text.html - |Formatter - java.util - |Formatter - java.util.logging - |FocusEvent - java.awt.event - |FontMetrics - java.awt - |Found - scala.collection.Searching - |""".stripMargin + extensionResult ) @Test def `extension-definition-select` = @@ -1569,7 +1479,6 @@ class CompletionSuite extends BaseCompletionSuite: | extension [T](x: Test.TestSel@@) |""".stripMargin, """|TestSelect[T] test.Test - |TestSelect test.Test |""".stripMargin ) @@ -1665,11 +1574,11 @@ class CompletionSuite extends BaseCompletionSuite: check( """import scala.collection.{AbstractMap, @@} |""".stripMargin, - """GenIterable scala.collection - |GenMap scala.collection - |GenSeq scala.collection - |GenSet scala.collection - |GenTraversable scala.collection + """+: scala.collection + |:+ scala.collection + |AbstractIndexedSeqView scala.collection + |AbstractIterable scala.collection + |AbstractIterator scala.collection |""".stripMargin, topLines = Some(5) ) @@ -1729,7 +1638,10 @@ class CompletionSuite extends BaseCompletionSuite: | List@@ |""".stripMargin, """|List[A](elems: A*): List[A] - |ListMap[K, V](elems: (K, V)*): ListMap[K, V] + |ListMap[K, V](elems: (K, V)*): ListMap[K, V] - scala.collection.immutable + |new ListMap[K, V]: ListMap[K, V] - scala.collection.immutable + |new ListSet[A]: ListSet[A] - scala.collection.immutable + |new ListMap[K, V]: ListMap[K, V] - scala.collection.mutable |""".stripMargin, filter = _.contains("[") ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala index 52e565a5a78b..cbc46f207634 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala @@ -700,7 +700,7 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: |object Main { | val a = ListBuffer($0) |}""".stripMargin, - filter = _.contains("[A]") + filter = _.startsWith("ListBuffer[A]") ) @Test def `type-import` = @@ -811,7 +811,7 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: |""".stripMargin, """|fooBar: String |fooBar: List[Int] - |fooBar(n: Int): Int + |fooBar(n: Int): Int - test.A |""".stripMargin, ) @@ -827,8 +827,9 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: | |val j = MyTy@@ |""".stripMargin, - """|MyType(m: Long): MyType - |MyType - demo.other""".stripMargin, + """|MyType - demo.other + |MyType(m: Long): MyType - demo.other + """.stripMargin, ) @Test def `type-apply2` = @@ -843,8 +844,9 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: | |val j = MyTy@@ |""".stripMargin, - """|MyType(m: Long): MyType - |MyType - demo.other""".stripMargin, + """|MyType - demo.other + |MyType(m: Long): MyType - demo.other + """.stripMargin, ) @Test def `method-name-conflict` = From 83e8ef78ede655a6e8e6186dbf37d41818d0e18e Mon Sep 17 00:00:00 2001 From: rochala Date: Mon, 18 Mar 2024 14:59:10 +0100 Subject: [PATCH 03/10] Remove unused imports, fields --- .../dotty/tools/pc/MetalsInteractive.scala | 2 +- .../dotty/tools/pc/PcInlayHintsProvider.scala | 1 - .../tools/pc/SignatureHelpProvider.scala | 6 ------ .../pc/completions/CompletionProvider.scala | 6 +++--- .../pc/completions/CompletionValue.scala | 19 +++++++++--------- .../tools/pc/completions/Completions.scala | 20 +++---------------- .../completions/InterpolatorCompletions.scala | 2 -- .../pc/completions/MatchCaseCompletions.scala | 2 -- .../pc/completions/NamedArgCompletions.scala | 3 +-- 9 files changed, 18 insertions(+), 43 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala b/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala index 381e0eaec6a5..648c59725742 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/MetalsInteractive.scala @@ -5,7 +5,7 @@ import scala.annotation.tailrec import dotc.* import ast.*, tpd.* -import core.*, Contexts.*, Decorators.*, Flags.*, Names.*, Symbols.*, Types.* +import core.*, Contexts.*, Flags.*, Names.*, Symbols.*, Types.* import interactive.* import util.* import util.SourcePosition diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index 9b40f1e6777a..6d634f56363c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -24,7 +24,6 @@ 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.IndexedContext import org.eclipse.lsp4j.InlayHint import org.eclipse.lsp4j.InlayHintKind diff --git a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala index f7797efbfb27..80317185458b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala @@ -6,20 +6,14 @@ import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.interactive.InteractiveDriver -import dotty.tools.dotc.parsing.Tokens.closingRegionTokens -import dotty.tools.dotc.reporting.ErrorMessageID -import dotty.tools.dotc.reporting.ExpectedTokenButFound import dotty.tools.dotc.util.Signatures import dotty.tools.dotc.util.SourceFile -import dotty.tools.dotc.util.Spans -import dotty.tools.dotc.util.Spans.Span import dotty.tools.pc.printer.ShortenedTypePrinter import dotty.tools.pc.printer.ShortenedTypePrinter.IncludeDefaultParam import dotty.tools.pc.utils.MtagsEnrichments.* import org.eclipse.lsp4j as l import scala.jdk.CollectionConverters.* -import scala.jdk.OptionConverters.* import scala.meta.internal.metals.ReportContext import scala.meta.pc.OffsetParams import scala.meta.pc.SymbolDocumentation diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index fb57a7cdabc5..a94f3bf1c371 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -196,8 +196,8 @@ class CompletionProvider( item end mkItem - val completionTextSuffix = completion.snippetSuffix.toSuffix - val completionTextPrefix = completion.snippetSuffix.toPrefix + val completionTextSuffix = completion.snippetAffix.toSuffix + val completionTextPrefix = completion.snippetAffix.toPrefix lazy val isInStringInterpolation = path match @@ -280,7 +280,7 @@ class CompletionProvider( case _ => val nameText = completion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) val nameWithAffixes = completionTextPrefix + nameText + completionTextSuffix - val insertText = if completion.snippetSuffix.nonEmpty && isInStringInterpolation then + val insertText = if completion.snippetAffix.nonEmpty && isInStringInterpolation then "{" + nameWithAffixes + "}" else nameWithAffixes mkItem(insertText, range = completion.range) 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 d623d1f434f1..0a94da66bb58 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala @@ -40,7 +40,7 @@ enum CompletionSource: sealed trait CompletionValue: def label: String def insertText: Option[String] = None - def snippetSuffix: CompletionAffix = CompletionAffix.empty + def snippetAffix: CompletionAffix = CompletionAffix.empty def additionalEdits: List[TextEdit] = Nil def range: Option[Range] = None def filterText: Option[String] = None @@ -96,7 +96,7 @@ object CompletionValue: override def labelWithDescription( printer: ShortenedTypePrinter )(using Context): String = - if symbol.isConstructor then s"${snippetSuffix.toPrefix}${label}${description(printer)}" + if symbol.isConstructor then s"${snippetAffix.toPrefix}${label}${description(printer)}" else if symbol.is(Method) then s"${label}${description(printer)}" else if symbol.is(Mutable) then s"$label: ${description(printer)}" else if symbol.is(Package) || symbol.is(Module) || symbol.isClass then @@ -107,7 +107,7 @@ object CompletionValue: else s"$label: ${description(printer)}" protected def labelWithSuffix(printer: ShortenedTypePrinter)(using Context): String = - if snippetSuffix.addLabelSnippet + if snippetAffix.addLabelSnippet then val printedParams = symbol.info.typeParams.map(p => p.paramName.decoded ++ printer.tpe(p.paramInfo) @@ -123,28 +123,28 @@ object CompletionValue: case class Compiler( label: String, denotation: Denotation, - override val snippetSuffix: CompletionAffix + override val snippetAffix: CompletionAffix ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.CompilerKind.ordinal case class Scope( label: String, denotation: Denotation, - override val snippetSuffix: CompletionAffix, + override val snippetAffix: CompletionAffix, ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.ScopeKind.ordinal case class Workspace( label: String, denotation: Denotation, - override val snippetSuffix: CompletionAffix, + override val snippetAffix: CompletionAffix, override val importSymbol: Symbol ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.WorkspaceKind.ordinal override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = if symbol.isConstructor || symbol.name == nme.apply then - s"${snippetSuffix.toPrefix}${label}${description(printer)} - ${printer.fullNameString(importSymbol.effectiveOwner)}" + s"${snippetAffix.toPrefix}${label}${description(printer)} - ${printer.fullNameString(importSymbol.effectiveOwner)}" else if symbol.is(Method) then s"${labelWithSuffix(printer)} - ${printer.fullNameString(symbol.effectiveOwner)}" else if symbol.is(Package) || symbol.is(Module) || symbol.isClass then @@ -157,7 +157,7 @@ object CompletionValue: case class ImplicitClass( label: String, denotation: Denotation, - override val snippetSuffix: CompletionAffix, + override val snippetAffix: CompletionAffix, override val importSymbol: Symbol, ) extends Symbolic: override def completionItemKind(using Context): CompletionItemKind = @@ -172,7 +172,7 @@ object CompletionValue: case class Extension( label: String, denotation: Denotation, - override val snippetSuffix: CompletionAffix + override val snippetAffix: CompletionAffix ) extends Symbolic: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Method @@ -257,6 +257,7 @@ object CompletionValue: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Folder + // TODO remove this type and return `Compiler`, `Workspace` instead case class Interpolator( denotation: Denotation, label: String, 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 ef81416480b4..5ddcced48ade 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -13,7 +13,6 @@ import scala.meta.pc.* import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.ast.untpd -import dotty.tools.dotc.ast.NavigateAST import dotty.tools.dotc.core.Comments.Comment import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.* @@ -34,18 +33,6 @@ import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor import dotty.tools.pc.buildinfo.BuildInfo import dotty.tools.pc.utils.MtagsEnrichments.* import dotty.tools.dotc.core.Denotations.SingleDenotation -import dotty.tools.dotc.interactive.Interactive - -import java.nio.file.Path -import java.nio.file.Paths -import scala.collection.mutable -import scala.meta.internal.metals.ReportContext -import scala.meta.internal.mtags.CoursierComplete -import scala.meta.internal.pc.CompletionFuzzy -import scala.meta.internal.pc.IdentifierComparator -import scala.meta.internal.pc.MemberOrdering -import scala.meta.pc.* -import scala.collection.concurrent.TrieMap class Completions( text: String, @@ -138,7 +125,6 @@ class Completions( case Select(qual, _) :: _ if qual.typeOpt.isErroneous => (allAdvanced, SymbolSearch.Result.COMPLETE) case Select(qual, _) :: _ => - val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath) val (compiler, result) = enrichedCompilerCompletions(qual.typeOpt.widenDealias) (allAdvanced ++ compiler, result) case _ => @@ -531,7 +517,7 @@ class Completions( CompletionValue.Workspace( label = undoBacktick(sym.decodedName), denotation = sym, - snippetSuffix = CompletionAffix.empty, + snippetAffix = CompletionAffix.empty, importSymbol = sym ) ) @@ -618,7 +604,7 @@ class Completions( symbolicCompletionsMap.foreach: (name, denots) => lazy val existsTypeWithSuffix: Boolean = symbolicCompletionsMap .get(name.toTypeName) - .forall(_.forall(sym => sym.snippetSuffix.suffixes.nonEmpty)) + .forall(_.forall(sym => sym.snippetAffix.suffixes.nonEmpty)) if completionMode.is(Mode.Term) && !completionMode.is(Mode.ImportOrExport) then typeResultMappings += name -> denots @@ -648,7 +634,7 @@ class Completions( val sym = symOnly.symbol val name = SemanticdbSymbols.symbolName(sym) val suffix = - if symOnly.snippetSuffix.addLabelSnippet then "[]" else "" + if symOnly.snippetAffix.addLabelSnippet then "[]" else "" val id = name + suffix val include = includeSymbol(sym) (id, include) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala index 50dd462bb028..dd032928f0d1 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala @@ -12,9 +12,7 @@ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Symbols.Symbol -import dotty.tools.dotc.util.Spans import dotty.tools.dotc.core.Types.Type -import dotty.tools.dotc.util.SourcePosition import dotty.tools.pc.CompilerSearchVisitor import dotty.tools.pc.IndexedContext import dotty.tools.pc.utils.MtagsEnrichments.* 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 7f1d92305309..908865124f58 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/MatchCaseCompletions.scala @@ -15,7 +15,6 @@ import dotty.tools.toOption import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.Definitions import dotty.tools.dotc.core.Denotations.Denotation import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Flags.* @@ -24,7 +23,6 @@ import dotty.tools.dotc.core.Symbols.NoSymbol import dotty.tools.dotc.core.Symbols.Symbol import dotty.tools.dotc.core.Types.AndType import dotty.tools.dotc.core.Types.ClassInfo -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 diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala index 6f244d9a3414..8ac5ef64af10 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/NamedArgCompletions.scala @@ -2,7 +2,6 @@ package dotty.tools.pc.completions import scala.util.Try -import dotty.tools.dotc.ast.NavigateAST import dotty.tools.dotc.ast.Trees.ValDef import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.ast.untpd @@ -439,4 +438,4 @@ case class JustSymbol(symbol: Symbol)(using Context) extends ParamSymbol: def info: Type = symbol.info case class RefinedSymbol(symbol: Symbol, name: Name, info: Type) - extends ParamSymbol \ No newline at end of file + extends ParamSymbol From a55824e895ca467b7b85c263154d242bdf2a022a Mon Sep 17 00:00:00 2001 From: rochala Date: Wed, 10 Apr 2024 13:58:13 +0200 Subject: [PATCH 04/10] Apply comments, make new completions work with fully qualified path prefix --- .../pc/completions/CompletionAffix.scala | 75 ++++++++++++++----- .../pc/completions/CompletionProvider.scala | 2 +- .../pc/completions/CompletionValue.scala | 3 + .../tools/pc/completions/Completions.scala | 54 ++++++++----- .../completions/InterpolatorCompletions.scala | 4 +- .../CompletionExtraConstructorSuite.scala | 24 ++++++ .../CompletionInterpolatorSuite.scala | 1 - .../completion/CompletionSnippetSuite.scala | 13 +++- 8 files changed, 133 insertions(+), 43 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala index 456f5cf5b12b..698aa1a6418d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala @@ -1,52 +1,86 @@ package dotty.tools.pc.completions +import org.eclipse.lsp4j.TextEdit +import org.eclipse.lsp4j.Position + /** * @param suffixes which we should insert + * @param prefixes which we should insert * @param snippet which suffix should we insert the snippet $0 */ case class CompletionAffix( - suffixes: Set[SuffixKind], - prefixes: List[PrefixKind], - snippet: SuffixKind, + suffixes: Set[Suffix], + prefixes: Set[Prefix], + snippet: Suffix, + currentPrefix: Option[String], ): - def addLabelSnippet = suffixes.contains(SuffixKind.Bracket) - def hasSnippet = snippet != SuffixKind.NoSuffix + def addLabelSnippet = suffixes.exists(_.kind == SuffixKind.Bracket) + def hasSnippet = snippet.kind != SuffixKind.NoSuffix def chain(copyFn: CompletionAffix => CompletionAffix) = copyFn(this) - def withNewSuffix(kind: SuffixKind) = this.copy(suffixes = suffixes + kind) - def withNewPrefix(kind: PrefixKind) = this.copy(prefixes = prefixes :+ kind) - def withNewSuffixSnippet(kind: SuffixKind) = - this.copy(suffixes = suffixes + kind, snippet = kind) + def withNewSuffix(kind: Suffix) = this.copy(suffixes = suffixes + kind) + def withNewPrefix(kind: Prefix) = this.copy(prefixes = prefixes + kind) + def withCurrentPrefix(currentPrefix: String) = this.copy(currentPrefix = Some(currentPrefix)) + def withNewSuffixSnippet(suffix: Suffix) = + this.copy(suffixes = suffixes + suffix, snippet = suffix) def nonEmpty: Boolean = suffixes.nonEmpty || prefixes.nonEmpty def toSuffix: String = def loop(suffixes: List[SuffixKind]): String = - def cursor = if suffixes.head == snippet then "$0" else "" + def cursor = if suffixes.head == snippet.kind then "$0" else "" suffixes match case SuffixKind.Brace :: tail => s"($cursor)" + loop(tail) case SuffixKind.Bracket :: tail => s"[$cursor]" + loop(tail) case SuffixKind.Template :: tail => s" {$cursor}" + loop(tail) case _ => "" - loop(suffixes.toList) + loop(suffixes.toList.map(_.kind)) def toSuffixOpt: Option[String] = val edit = toSuffix if edit.nonEmpty then Some(edit) else None + + given Ordering[Position] = Ordering.by(elem => (elem.getLine, elem.getCharacter)) + + def toInsertRange: Option[org.eclipse.lsp4j.Range] = + import scala.language.unsafeNulls + + val ranges = prefixes.collect: + case Affix(_, Some(range)) => range + .toList + for + startPos <- ranges.map(_.getStart).minOption + endPos <- ranges.map(_.getEnd).maxOption + yield org.eclipse.lsp4j.Range(startPos, endPos) + + private def loopPrefix(prefixes: List[PrefixKind]) = + prefixes match + case PrefixKind.New :: tail => "new " + case _ => "" + + /** + * We need to insert previous prefix, but we don't want to display it in the label i.e. + * ```scala + * scala.util.Tr@@ + * ```` + * should return `new Try[T]: Try[T]` + * but insert `new scala.util.Try` + * + */ + def toInsertPrefix: String = + loopPrefix(prefixes.toList.map(_.kind)) + currentPrefix.getOrElse("") + def toPrefix: String = - def loop(prefixes: List[PrefixKind]) = - prefixes match - case PrefixKind.New :: tail => "new " - case _ => "" - loop(prefixes) + loopPrefix(prefixes.toList.map(_.kind)) end CompletionAffix object CompletionAffix: val empty = CompletionAffix( suffixes = Set.empty, - prefixes = Nil, - snippet = SuffixKind.NoSuffix, + prefixes = Set.empty, + snippet = Affix(SuffixKind.NoSuffix), + currentPrefix = None, ) enum SuffixKind: @@ -54,3 +88,8 @@ enum SuffixKind: enum PrefixKind: case New + +type Suffix = Affix[SuffixKind] +type Prefix = Affix[PrefixKind] + +private case class Affix[+T](kind: T, insertRange: Option[org.eclipse.lsp4j.Range] = None) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index a94f3bf1c371..eae4bce7add0 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -197,7 +197,7 @@ class CompletionProvider( end mkItem val completionTextSuffix = completion.snippetAffix.toSuffix - val completionTextPrefix = completion.snippetAffix.toPrefix + val completionTextPrefix = completion.snippetAffix.toInsertPrefix lazy val isInStringInterpolation = path match 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 0a94da66bb58..6877bab5ea8b 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala @@ -79,6 +79,9 @@ object CompletionValue: ) def importSymbol: Symbol = symbol + override def range: Option[Range] = + snippetAffix.toInsertRange + def completionItemKind(using Context): CompletionItemKind = val symbol = this.symbol if symbol.is(Package) || symbol.is(Module) then 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 5ddcced48ade..305aed8b1505 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -33,6 +33,8 @@ import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor import dotty.tools.pc.buildinfo.BuildInfo import dotty.tools.pc.utils.MtagsEnrichments.* import dotty.tools.dotc.core.Denotations.SingleDenotation +import org.eclipse.lsp4j.TextEdit +import org.eclipse.lsp4j.Position class Completions( text: String, @@ -144,7 +146,7 @@ class Completions( denots: Seq[SingleDenotation] ): List[CompletionValue] = denots.toList.flatMap: denot => - completionsWithSuffix( + completionsWithAffix( denot, completion.show, (label, denot, suffix) => CompletionValue.Compiler(label, denot, suffix) @@ -189,7 +191,7 @@ class Completions( CompletionAffix.empty .chain { suffix => // for [] suffix if shouldAddSnippet && symbol.info.typeParams.nonEmpty then - suffix.withNewSuffixSnippet(SuffixKind.Bracket) + suffix.withNewSuffixSnippet(Affix(SuffixKind.Bracket)) else suffix } .chain { suffix => // for () suffix @@ -197,7 +199,7 @@ class Completions( val paramss = getParams(symbol) paramss match case Nil => suffix - case List(Nil) => suffix.withNewSuffix(SuffixKind.Brace) + case List(Nil) => suffix.withNewSuffix(Affix(SuffixKind.Brace)) case _ if config.isCompletionSnippetsEnabled() => val onlyParameterless = paramss.forall(_.isEmpty) lazy val onlyImplicitOrTypeParams = paramss.forall( @@ -205,28 +207,27 @@ class Completions( sym.isType || sym.is(Implicit) || sym.is(Given) } ) - if onlyParameterless then suffix.withNewSuffix(SuffixKind.Brace) + if onlyParameterless then suffix.withNewSuffix(Affix(SuffixKind.Brace)) else if onlyImplicitOrTypeParams then suffix - else if suffix.hasSnippet then - suffix.withNewSuffix(SuffixKind.Brace) - else suffix.withNewSuffixSnippet(SuffixKind.Brace) + else if suffix.hasSnippet then suffix.withNewSuffix(Affix(SuffixKind.Brace)) + else suffix.withNewSuffixSnippet(Affix(SuffixKind.Brace)) case _ => suffix end match else suffix } .chain { suffix => // for {} suffix if shouldAddSnippet && isNew && isAbstractType(symbol) then - if suffix.hasSnippet then suffix.withNewSuffix(SuffixKind.Template) - else suffix.withNewSuffixSnippet(SuffixKind.Template) + if suffix.hasSnippet then suffix.withNewSuffix(Affix(SuffixKind.Template)) + else suffix.withNewSuffixSnippet(Affix(SuffixKind.Template)) else suffix } end findSuffix - def completionsWithSuffix( + def completionsWithAffix( denot: SingleDenotation, label: String, - toCompletionValue: (String, SingleDenotation, CompletionAffix) => CompletionValue + toCompletionValue: (String, SingleDenotation, CompletionAffix) => CompletionValue.Symbolic ): List[CompletionValue] = val sym = denot.symbol val hasNonSyntheticConstructor = sym.name.isTypeName && sym.isClass @@ -258,17 +259,30 @@ class Completions( else denot :: Nil - val requiresInitDisambiguiation = methodDenots.exists(_.symbol.isConstructor) && methodDenots.exists(_.symbol.name == nme.apply) + val existsApply = methodDenots.exists(_.symbol.name == nme.apply) methodDenots.map { methodDenot => val suffix = findSuffix(methodDenot.symbol) - val affix = if methodDenot.symbol.isConstructor && requiresInitDisambiguiation then - suffix.withNewPrefix(PrefixKind.New) + val currentPrefix = adjustedPath match + case Select(qual, _) :: _ => Some(qual.show + ".", qual.span.start) + case _ => None + + val affix = if methodDenot.symbol.isConstructor && existsApply then + adjustedPath match + case (select @ Select(qual, _)) :: _ => + val start = qual.span.start + val insertRange = select.sourcePos.startPos.withEnd(completionPos.queryEnd).toLsp + + suffix + .withCurrentPrefix(qual.show + ".") + .withNewPrefix(Affix(PrefixKind.New, insertRange = Some(insertRange))) + case _ => + suffix.withNewPrefix(Affix(PrefixKind.New)) else suffix val name = undoBacktick(label) toCompletionValue(name, methodDenot, affix) } - end completionsWithSuffix + end completionsWithAffix /** * @return Tuple of completionValues and flag. If the latter boolean value is true @@ -522,7 +536,7 @@ class Completions( ) ) case _ => - completionsWithSuffix( + completionsWithAffix( sym, sym.decodedName, CompletionValue.Workspace(_, _, _, sym) @@ -555,13 +569,13 @@ class Completions( && !sym.isConstructor && !isDefaultVariableSetter if isExtensionMethod then - completionsWithSuffix( + completionsWithAffix( sym, sym.decodedName, CompletionValue.Extension(_, _, _) ).map(visit).forall(_ == true) else if isImplicitClassMember then - completionsWithSuffix( + completionsWithAffix( sym, sym.decodedName, CompletionValue.ImplicitClass(_, _, _, sym.maybeOwner), @@ -602,7 +616,7 @@ class Completions( .groupBy(_.symbol.fullName) // we somehow have to ignore proxy type symbolicCompletionsMap.foreach: (name, denots) => - lazy val existsTypeWithSuffix: Boolean = symbolicCompletionsMap + lazy val existsTypeWithoutSuffix: Boolean = !symbolicCompletionsMap .get(name.toTypeName) .forall(_.forall(sym => sym.snippetAffix.suffixes.nonEmpty)) @@ -610,7 +624,7 @@ class Completions( typeResultMappings += name -> denots // show non synthetic symbols // companion test should not result TrieMap[K, V] - else if name.isTermName && existsTypeWithSuffix then + else if name.isTermName && !existsTypeWithoutSuffix then typeResultMappings += name -> denots else if name.isTypeName then typeResultMappings += name -> denots diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala index dd032928f0d1..266e3c97a279 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala @@ -153,7 +153,7 @@ object InterpolatorCompletions: sym.name.toString() ) => val label = sym.name.decoded - completions.completionsWithSuffix( + completions.completionsWithAffix( sym, label, (name, denot, suffix) => @@ -284,7 +284,7 @@ object InterpolatorCompletions: sym.name.decoded ) && !sym.isType => val label = sym.name.decoded - completions.completionsWithSuffix( + completions.completionsWithAffix( sym, label, (name, denot, suffix) => diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala index 963b563307b1..6e490ea2db01 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala @@ -512,3 +512,27 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: includeCompletionKind = true ) + @Test def `prepend-new` = + checkSnippet( + """|object Wrapper: + | Try@@ + | + |""".stripMargin, + """|Try + |Try($0) + |new Try + |""".stripMargin, + ) + + @Test def `prepend-new-fully-qualified-path` = + checkSnippet( + """|object Wrapper: + | scala.util.Try@@ + | + |""".stripMargin, + """|Try + |Try($0) + |new scala.util.Try + |""".stripMargin, + ) + diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala index 95997718a4d7..cd77537f1464 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala @@ -626,7 +626,6 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: | s"this is an interesting ${java.nio.file.Paths}" |} |""".stripMargin, - itemIndex = 0, assertSingleItem = false, ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala index e731d95b8987..f28a014fe5ee 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala @@ -310,10 +310,21 @@ class CompletionSnippetSuite extends BaseCompletionSuite: |""".stripMargin, """|Try |Try($0) - |new Try + |new scala.util.Try |""".stripMargin ) + @Test def `case-class2-edit` = + checkEditLine( + s"""|object Main { + | ___ + |} + |""".stripMargin, + "scala.util.Tr@@", + "new scala.util.Try", + filter = _.contains("new Try") + ) + @Test def `case-class3` = checkSnippet( s"""|object Main { From 407e16dc73dad43632c0dc1a16244740d8d1e3c3 Mon Sep 17 00:00:00 2001 From: rochala Date: Wed, 10 Apr 2024 14:00:42 +0200 Subject: [PATCH 05/10] remove unused imports --- .../main/dotty/tools/pc/completions/CompletionAffix.scala | 8 ++++---- .../src/main/dotty/tools/pc/completions/Completions.scala | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala index 698aa1a6418d..98fab25a93c9 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala @@ -1,7 +1,7 @@ package dotty.tools.pc.completions -import org.eclipse.lsp4j.TextEdit import org.eclipse.lsp4j.Position +import org.eclipse.lsp4j.Range /** * @param suffixes which we should insert @@ -42,7 +42,7 @@ case class CompletionAffix( given Ordering[Position] = Ordering.by(elem => (elem.getLine, elem.getCharacter)) - def toInsertRange: Option[org.eclipse.lsp4j.Range] = + def toInsertRange: Option[Range] = import scala.language.unsafeNulls val ranges = prefixes.collect: @@ -51,7 +51,7 @@ case class CompletionAffix( for startPos <- ranges.map(_.getStart).minOption endPos <- ranges.map(_.getEnd).maxOption - yield org.eclipse.lsp4j.Range(startPos, endPos) + yield Range(startPos, endPos) private def loopPrefix(prefixes: List[PrefixKind]) = prefixes match @@ -92,4 +92,4 @@ enum PrefixKind: type Suffix = Affix[SuffixKind] type Prefix = Affix[PrefixKind] -private case class Affix[+T](kind: T, insertRange: Option[org.eclipse.lsp4j.Range] = None) +private case class Affix[+T](kind: T, insertRange: Option[Range] = None) 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 305aed8b1505..96fcf4ac0292 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -33,8 +33,7 @@ import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor import dotty.tools.pc.buildinfo.BuildInfo import dotty.tools.pc.utils.MtagsEnrichments.* import dotty.tools.dotc.core.Denotations.SingleDenotation -import org.eclipse.lsp4j.TextEdit -import org.eclipse.lsp4j.Position + class Completions( text: String, From f89448ecc2cb228f2f4c8fec00e7c21e1a15de32 Mon Sep 17 00:00:00 2001 From: rochala Date: Wed, 10 Apr 2024 20:58:09 +0200 Subject: [PATCH 06/10] Fix missing braces in interpolator, apply last review --- .../pc/completions/CompletionAffix.scala | 4 +- .../pc/completions/CompletionProvider.scala | 47 ++++++++++--------- .../tools/pc/completions/Completions.scala | 19 +++----- .../completions/InterpolatorCompletions.scala | 29 ++++++------ .../CompletionInterpolatorSuite.scala | 24 ++++++++++ 5 files changed, 71 insertions(+), 52 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala index 98fab25a93c9..4ed58c773a7c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala @@ -53,9 +53,9 @@ case class CompletionAffix( endPos <- ranges.map(_.getEnd).maxOption yield Range(startPos, endPos) - private def loopPrefix(prefixes: List[PrefixKind]) = + private def loopPrefix(prefixes: List[PrefixKind]): String = prefixes match - case PrefixKind.New :: tail => "new " + case PrefixKind.New :: tail => "new " + loopPrefix(tail) case _ => "" /** diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index eae4bce7add0..25d37fc1160f 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -161,6 +161,25 @@ class CompletionProvider( val label = completion.labelWithDescription(printer) val ident = completion.insertText.getOrElse(completion.label) + lazy val isInStringInterpolation = + path match + // s"My name is $name" + case (_: Ident) :: (_: SeqLiteral) :: (_: Typed) :: Apply( + Select(Apply(Select(Select(_, name), _), _), _), + _ + ) :: _ => + name == StdNames.nme.StringContext + // "My name is $name" + case Literal(Constant(_: String)) :: _ => + true + case _ => + false + + def wrapInBracketsIfRequired(newText: String): String = + if completion.snippetAffix.nonEmpty && isInStringInterpolation then + "{" + newText + "}" + else newText + def mkItem( newText: String, additionalEdits: List[TextEdit] = Nil, @@ -170,7 +189,7 @@ class CompletionProvider( val editRange = if newText.startsWith(oldText) then completionPos.stripSuffixEditRange else completionPos.toEditRange - val textEdit = new TextEdit(range.getOrElse(editRange), newText) + val textEdit = new TextEdit(range.getOrElse(editRange), wrapInBracketsIfRequired(newText)) val item = new CompletionItem(label) item.setSortText(f"${idx}%05d") @@ -199,20 +218,6 @@ class CompletionProvider( val completionTextSuffix = completion.snippetAffix.toSuffix val completionTextPrefix = completion.snippetAffix.toInsertPrefix - lazy val isInStringInterpolation = - path match - // s"My name is $name" - case (_: Ident) :: (_: SeqLiteral) :: (_: Typed) :: Apply( - Select(Apply(Select(Select(_, name), _), _), _), - _ - ) :: _ => - name == StdNames.nme.StringContext - // "My name is $name" - case Literal(Constant(_: String)) :: _ => - true - case _ => - false - lazy val backtickSoftKeyword = path match case (_: Select) :: _ => false case _ => true @@ -243,13 +248,12 @@ class CompletionProvider( case IndexedContext.Result.InScope => mkItem( v.insertText.getOrElse( - completionTextPrefix + - ident.backticked( - backtickSoftKeyword - ) + completionTextSuffix + completionTextPrefix + ident.backticked(backtickSoftKeyword) + completionTextSuffix ), range = v.range, ) + // Special case when symbol is out of scope, and there is no auto import. + // It means that it will use fully qualified path case _ if isInStringInterpolation => mkItem( "{" + completionTextPrefix + sym.fullNameBackticked + completionTextSuffix + "}", @@ -280,10 +284,7 @@ class CompletionProvider( case _ => val nameText = completion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) val nameWithAffixes = completionTextPrefix + nameText + completionTextSuffix - val insertText = if completion.snippetAffix.nonEmpty && isInStringInterpolation then - "{" + nameWithAffixes + "}" - else nameWithAffixes - mkItem(insertText, range = completion.range) + mkItem(nameWithAffixes, range = completion.range) end match end completionItems 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 96fcf4ac0292..12385de40bba 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -262,10 +262,6 @@ class Completions( methodDenots.map { methodDenot => val suffix = findSuffix(methodDenot.symbol) - val currentPrefix = adjustedPath match - case Select(qual, _) :: _ => Some(qual.show + ".", qual.span.start) - case _ => None - val affix = if methodDenot.symbol.isConstructor && existsApply then adjustedPath match case (select @ Select(qual, _)) :: _ => @@ -606,7 +602,6 @@ class Completions( /** If we try to complete TypeName, we should favor types over terms with same name value and without suffix. */ def deduplicateCompletions(completions: List[CompletionValue]): List[CompletionValue] = - val typeResultMappings = mutable.Map.empty[Name, Seq[CompletionValue]] val (symbolicCompletions, rest) = completions.partition: _.isInstanceOf[CompletionValue.Symbolic] @@ -614,21 +609,19 @@ class Completions( .collect { case symbolic: CompletionValue.Symbolic => symbolic } .groupBy(_.symbol.fullName) // we somehow have to ignore proxy type - symbolicCompletionsMap.foreach: (name, denots) => + val filteredSymbolicCompletions = symbolicCompletionsMap.filter: (name, denots) => lazy val existsTypeWithoutSuffix: Boolean = !symbolicCompletionsMap .get(name.toTypeName) .forall(_.forall(sym => sym.snippetAffix.suffixes.nonEmpty)) - if completionMode.is(Mode.Term) && !completionMode.is(Mode.ImportOrExport) then - typeResultMappings += name -> denots + (completionMode.is(Mode.Term) && !completionMode.is(Mode.ImportOrExport)) || // show non synthetic symbols // companion test should not result TrieMap[K, V] - else if name.isTermName && !existsTypeWithoutSuffix then - typeResultMappings += name -> denots - else if name.isTypeName then - typeResultMappings += name -> denots + (name.isTermName && !existsTypeWithoutSuffix) || + name.isTypeName + .toList.unzip._2.flatten - typeResultMappings.toList.unzip._2.flatten ++ rest + filteredSymbolicCompletions ++ rest extension (l: List[CompletionValue]) def filterInteresting( diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala index 266e3c97a279..9c973e6e63e0 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala @@ -110,18 +110,17 @@ object InterpolatorCompletions: buildTargetIdentifier: String )(using Context, ReportContext): List[CompletionValue] = def newText( - name: String, - suffix: Option[String], + label: String, + affix: CompletionAffix , identOrSelect: Ident | Select ): String = - val snippetCursor = suffixEnding(suffix, areSnippetsSupported) + val snippetCursor = suffixEnding(affix.toSuffixOpt, areSnippetsSupported) new StringBuilder() .append('{') - .append( - text.substring(identOrSelect.span.start, identOrSelect.span.end) - ) + .append(affix.toPrefix) // we use toPrefix here, because previous prefix is added in the next step + .append(text.substring(identOrSelect.span.start, identOrSelect.span.end)) .append('.') - .append(name.backticked) + .append(label.backticked) .append(snippetCursor) .append('}') .toString @@ -156,11 +155,11 @@ object InterpolatorCompletions: completions.completionsWithAffix( sym, label, - (name, denot, suffix) => + (name, denot, affix) => CompletionValue.Interpolator( denot.symbol, label, - Some(newText(name, suffix.toSuffixOpt, identOrSelect)), + Some(newText(name, affix, identOrSelect)), Nil, Some(completionPos.originalCursorPosition.withStart(identOrSelect.span.start).toLsp), // Needed for VS Code which will not show the completion otherwise @@ -250,16 +249,18 @@ object InterpolatorCompletions: interpolatorEdit ++ dollarEdits end additionalEdits - def newText(symbolName: String, suffix: Option[String]): String = + def newText(symbolName: String, affix: CompletionAffix): String = val out = new StringBuilder() val identifier = symbolName.backticked val symbolNeedsBraces = interpolator.needsBraces || identifier.startsWith("`") || - suffix.isDefined + affix.toSuffixOpt.isDefined || + affix.toPrefix.nonEmpty if symbolNeedsBraces && !hasOpeningBrace then out.append('{') + out.append(affix.toInsertPrefix) out.append(identifier) - out.append(suffixEnding(suffix, areSnippetsSupported)) + out.append(suffixEnding(affix.toSuffixOpt, areSnippetsSupported)) if symbolNeedsBraces && !hasClosingBrace then out.append('}') out.toString end newText @@ -287,11 +288,11 @@ object InterpolatorCompletions: completions.completionsWithAffix( sym, label, - (name, denot, suffix) => + (name, denot, affix) => CompletionValue.Interpolator( denot.symbol, label, - Some(newText(name, suffix.toSuffixOpt)), + Some(newText(name, affix)), additionalEdits(), Some(nameRange), None, diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala index cd77537f1464..eaaf0ed343c9 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala @@ -779,3 +779,27 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: |""".stripMargin, "host: String" ) + + @Test def `prepend-new-missing-interpolator` = + checkSnippet( + """|object Wrapper: + | "$Try@@" + | + |""".stripMargin, + """|Try$0 + |{Try($0)} + |{new Try$0} + |""".stripMargin, + ) + + @Test def `prepend-new-interpolator` = + checkSnippet( + """|object Wrapper: + | s"$Try@@" + | + |""".stripMargin, + """|Try + |{Try($0)} + |{new Try} + |""".stripMargin, + ) From e343d3673e9b04985f5470ddadf6a66fa996e37a Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 11 Apr 2024 16:22:16 +0200 Subject: [PATCH 07/10] Fix ordering and deduplication of methods coming from traits --- .../pc/completions/CompletionProvider.scala | 39 ++-- .../pc/completions/CompletionValue.scala | 32 ++++ .../tools/pc/completions/Completions.scala | 122 +++++++----- .../tests/completion/CompletionArgSuite.scala | 6 +- .../tests/completion/CompletionDocSuite.scala | 17 +- .../CompletionExtraConstructorSuite.scala | 176 ++++++++++-------- .../CompletionInterpolatorSuite.scala | 68 +++++-- .../completion/CompletionSnippetSuite.scala | 42 +++-- .../pc/tests/completion/CompletionSuite.scala | 22 ++- .../completion/CompletionWorkspaceSuite.scala | 10 +- 10 files changed, 331 insertions(+), 203 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index 25d37fc1160f..6cfd87773c8e 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -32,6 +32,7 @@ import org.eclipse.lsp4j.InsertTextFormat import org.eclipse.lsp4j.InsertTextMode import org.eclipse.lsp4j.Range as LspRange import org.eclipse.lsp4j.TextEdit +import dotty.tools.dotc.cc.CaptureSet.empty class CompletionProvider( search: SymbolSearch, @@ -153,13 +154,17 @@ class CompletionProvider( val printer = ShortenedTypePrinter(search, IncludeDefaultParam.ResolveLater)(using indexedContext) + val underlyingCompletion = completion match + case CompletionValue.ExtraMethod(_, underlying) => underlying + case other => other + // For overloaded signatures we get multiple symbols, so we need // to recalculate the description - // related issue https://github.com/scala/scala3/issues/11941 - lazy val kind: CompletionItemKind = completion.completionItemKind - val description = completion.description(printer) - val label = completion.labelWithDescription(printer) - val ident = completion.insertText.getOrElse(completion.label) + // related issue https://github.com/lampepfl/scala3/issues/11941 + lazy val kind: CompletionItemKind = underlyingCompletion.completionItemKind + val description = underlyingCompletion.description(printer) + val label = underlyingCompletion.labelWithDescription(printer) + val ident = underlyingCompletion.insertText.getOrElse(underlyingCompletion.label) lazy val isInStringInterpolation = path match @@ -176,7 +181,7 @@ class CompletionProvider( false def wrapInBracketsIfRequired(newText: String): String = - if completion.snippetAffix.nonEmpty && isInStringInterpolation then + if underlyingCompletion.snippetAffix.nonEmpty && isInStringInterpolation then "{" + newText + "}" else newText @@ -194,20 +199,20 @@ class CompletionProvider( val item = new CompletionItem(label) item.setSortText(f"${idx}%05d") item.setDetail(description) - item.setFilterText(completion.filterText.getOrElse(completion.label)) + item.setFilterText(underlyingCompletion.filterText.getOrElse(underlyingCompletion.label)) item.setTextEdit(textEdit) - item.setAdditionalTextEdits((completion.additionalEdits ++ additionalEdits).asJava) - completion.insertMode.foreach(item.setInsertTextMode) + item.setAdditionalTextEdits((underlyingCompletion.additionalEdits ++ additionalEdits).asJava) + underlyingCompletion.insertMode.foreach(item.setInsertTextMode) - val data = completion.completionData(buildTargetIdentifier) + val data = underlyingCompletion.completionData(buildTargetIdentifier) item.setData(data.toJson) - item.setTags(completion.lspTags.asJava) + item.setTags(underlyingCompletion.lspTags.asJava) if config.isCompletionSnippetsEnabled() then item.setInsertTextFormat(InsertTextFormat.Snippet) - completion.command.foreach { command => + underlyingCompletion.command.foreach { command => item.setCommand(new Command("", command)) } @@ -215,8 +220,8 @@ class CompletionProvider( item end mkItem - val completionTextSuffix = completion.snippetAffix.toSuffix - val completionTextPrefix = completion.snippetAffix.toInsertPrefix + val completionTextSuffix = underlyingCompletion.snippetAffix.toSuffix + val completionTextPrefix = underlyingCompletion.snippetAffix.toInsertPrefix lazy val backtickSoftKeyword = path match case (_: Select) :: _ => false @@ -276,15 +281,15 @@ class CompletionProvider( end match end mkItemWithImports - completion match + underlyingCompletion match case v: (CompletionValue.Workspace | CompletionValue.Extension | CompletionValue.ImplicitClass) => mkItemWithImports(v) case v: CompletionValue.Interpolator if v.isWorkspace || v.isExtension => mkItemWithImports(v) case _ => - val nameText = completion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) + val nameText = underlyingCompletion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) val nameWithAffixes = completionTextPrefix + nameText + completionTextSuffix - mkItem(nameWithAffixes, range = completion.range) + mkItem(nameWithAffixes, range = underlyingCompletion.range) end match end completionItems 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 6877bab5ea8b..e1877a1a9c88 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionValue.scala @@ -130,6 +130,38 @@ object CompletionValue: ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.CompilerKind.ordinal + /** + * We need to access original completion in sorting phase. + * This class is only a wrapper to hold both new completion and original completion. + * + * All methods are proxied to @param extraMethod + * + * FIXME Refactor this file to different architercture. At least to somethhing that is easier to modifiy and scale. + * One solution may be a migration to flag based solution. + */ + case class ExtraMethod( + owner: Denotation, + extraMethod: Symbolic, + ) extends Symbolic: + override def additionalEdits: List[TextEdit] = extraMethod.additionalEdits + override def command: Option[String] = extraMethod.command + override def completionData(buildTargetIdentifier: String)(using Context): CompletionItemData = extraMethod.completionData((buildTargetIdentifier)) + override def completionItemKind(using Context): CompletionItemKind = extraMethod.completionItemKind + override def description(printer: ShortenedTypePrinter)(using Context): String = extraMethod.description(printer) + override def labelWithDescription(printer: ShortenedTypePrinter)(using Context): String = extraMethod.labelWithDescription(printer) + override def range: Option[Range] = extraMethod.range + override def denotation: Denotation = extraMethod.denotation + override def label: String = extraMethod.label + override def filterText: Option[String] = extraMethod.filterText + override def importSymbol: Symbol = extraMethod.importSymbol + override def lspTags(using Context): List[CompletionItemTag] = extraMethod.lspTags + override def insertText: Option[String] = extraMethod.insertText + override def isExtensionMethod: Boolean = extraMethod.isExtensionMethod + override def snippetAffix: CompletionAffix = extraMethod.snippetAffix + override def insertMode: Option[InsertTextMode] = extraMethod.insertMode + override val symbol: Symbol = extraMethod.symbol + override def completionItemDataKind: Integer = extraMethod.completionItemDataKind + case class Scope( label: String, denotation: Denotation, 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 12385de40bba..3bb869782e14 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -8,7 +8,7 @@ import scala.collection.mutable import scala.meta.internal.metals.Fuzzy import scala.meta.internal.metals.ReportContext import scala.meta.internal.mtags.CoursierComplete -import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering} +import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering, CompletionFuzzy} import scala.meta.pc.* import dotty.tools.dotc.ast.tpd.* @@ -232,16 +232,18 @@ class Completions( val hasNonSyntheticConstructor = sym.name.isTypeName && sym.isClass && !sym.is(ModuleClass) && !sym.is(Trait) && !sym.is(Abstract) && !sym.is(Flags.JavaDefined) - val methodDenots: List[SingleDenotation] = + val (extraMethodDenots, skipOriginalDenot): (List[SingleDenotation], Boolean) = if shouldAddSnippet && isNew && hasNonSyntheticConstructor then - sym.info.member(nme.CONSTRUCTOR).allSymbols.map(_.asSingleDenotation) + val constructors = sym.info.member(nme.CONSTRUCTOR).allSymbols.map(_.asSingleDenotation) .filter(_.symbol.isAccessibleFrom(denot.info)) + constructors -> true + else if shouldAddSnippet && completionMode.is(Mode.Term) && sym.name.isTermName && !sym.is(Flags.Method) && !sym.is(Flags.JavaDefined) then val constructors = if sym.isAllOf(ConstructorProxyModule) then sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols else val companionApplies = denot.info.member(nme.apply).allSymbols - val classConstructors = if sym.companionClass.exists then + val classConstructors = if sym.companionClass.exists && !sym.companionClass.isOneOf(AbstractOrTrait) then sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols else Nil @@ -250,33 +252,44 @@ class Completions( else companionApplies ++ classConstructors - val extraDenots = constructors.map(_.asSeenFrom(denot.info).asSingleDenotation) + val result = constructors.map(_.asSeenFrom(denot.info).asSingleDenotation) .filter(_.symbol.isAccessibleFrom(denot.info)) - if sym.isAllOf(ConstructorProxyModule) || sym.is(Trait) then extraDenots - else denot :: extraDenots + result -> (sym.isAllOf(ConstructorProxyModule) || sym.is(Trait)) + else Nil -> false - else denot :: Nil + val extraCompletionValues = + val existsApply = extraMethodDenots.exists(_.symbol.name == nme.apply) - val existsApply = methodDenots.exists(_.symbol.name == nme.apply) + extraMethodDenots.map { methodDenot => + val suffix = findSuffix(methodDenot.symbol) + val affix = if methodDenot.symbol.isConstructor && existsApply then + adjustedPath match + case (select @ Select(qual, _)) :: _ => + val start = qual.span.start + val insertRange = select.sourcePos.startPos.withEnd(completionPos.queryEnd).toLsp - methodDenots.map { methodDenot => - val suffix = findSuffix(methodDenot.symbol) - val affix = if methodDenot.symbol.isConstructor && existsApply then - adjustedPath match - case (select @ Select(qual, _)) :: _ => - val start = qual.span.start - val insertRange = select.sourcePos.startPos.withEnd(completionPos.queryEnd).toLsp + suffix + .withCurrentPrefix(qual.show + ".") + .withNewPrefix(Affix(PrefixKind.New, insertRange = Some(insertRange))) + case _ => + suffix.withNewPrefix(Affix(PrefixKind.New)) + else suffix + val name = undoBacktick(label) - suffix - .withCurrentPrefix(qual.show + ".") - .withNewPrefix(Affix(PrefixKind.New, insertRange = Some(insertRange))) - case _ => - suffix.withNewPrefix(Affix(PrefixKind.New)) - else suffix + CompletionValue.ExtraMethod( + owner = denot, + extraMethod = toCompletionValue(name, methodDenot, affix) + ) + } + + if skipOriginalDenot then extraCompletionValues + else + val suffix = findSuffix(denot.symbol) val name = undoBacktick(label) - toCompletionValue(name, methodDenot, affix) - } + val denotCompletionValue = toCompletionValue(name, denot, suffix) + denotCompletionValue :: extraCompletionValues + end completionsWithAffix /** @@ -638,7 +651,10 @@ class Completions( case ck: CompletionValue.CaseKeyword => (ck.label, true) case symOnly: CompletionValue.Symbolic => val sym = symOnly.symbol - val name = SemanticdbSymbols.symbolName(sym) + val name = symOnly match + case CompletionValue.ExtraMethod(owner, extraMethod) => + SemanticdbSymbols.symbolName(owner.symbol) + SemanticdbSymbols.symbolName(extraMethod.symbol) + case _ => SemanticdbSymbols.symbolName(sym) val suffix = if symOnly.snippetAffix.addLabelSnippet then "[]" else "" val id = name + suffix @@ -749,18 +765,24 @@ class Completions( relevance end symbolRelevance + def computeRelevance(sym: Symbol, completionValue: CompletionValue.Symbolic) = + completionValue match + case _: CompletionValue.Override => + var penalty = symbolRelevance(sym) + // show the abstract members first + if !sym.is(Deferred) then penalty |= MemberOrdering.IsNotAbstract + penalty + case _: CompletionValue.Workspace => + symbolRelevance(sym) | (IsWorkspaceSymbol + sym.name.show.length()) + case _ => symbolRelevance(sym) + completion match - case ov: CompletionValue.Override => - var penalty = symbolRelevance(ov.symbol) - // show the abstract members first - if !ov.symbol.is(Deferred) then penalty |= MemberOrdering.IsNotAbstract - penalty - case CompletionValue.Workspace(_, denot, _, _) => - symbolRelevance(denot.symbol) | (IsWorkspaceSymbol + denot.name.show.length()) + case CompletionValue.ExtraMethod(owner, extraMethod) => + computeRelevance(owner.symbol, extraMethod) case sym: CompletionValue.Symbolic => - symbolRelevance(sym.symbol) - case _ => - Int.MaxValue + computeRelevance(sym.symbol, sym) + case _ => Int.MaxValue + end computeRelevancePenalty private lazy val isEvilMethod: Set[Name] = Set[Name]( @@ -868,6 +890,7 @@ class Completions( def priority(v: CompletionValue): Int = v match case _: CompletionValue.Compiler => 0 + case CompletionValue.ExtraMethod(_, _: CompletionValue.Compiler) => 0 case _ => 1 priority(o1) - priority(o2) @@ -909,12 +932,19 @@ class Completions( def methodScore(v: CompletionValue.Symbolic)(using Context): Int = val sym = v.symbol - val workspacePenalty = if v.isInstanceOf[CompletionValue.Workspace] then 3 else 0 + val workspacePenalty = v match + case CompletionValue.ExtraMethod(_, _: CompletionValue.Workspace) => 5 + case _: CompletionValue.Workspace => 5 + case _ => 0 + + val isExtraMethod = v.isInstanceOf[CompletionValue.ExtraMethod] val methodPenalty = if isNew && sym.isConstructor then -1 - else if !completionMode.is(Mode.Member) && sym.name == nme.apply then 1 - else if sym.isConstructor then 2 - else 0 + else if isExtraMethod && !sym.isConstructor then 1 + else if isExtraMethod then 2 + else if !sym.isAllOf(SyntheticModule) then 3 + else 4 + workspacePenalty + methodPenalty override def compare(o1: CompletionValue, o2: CompletionValue): Int = @@ -942,14 +972,14 @@ class Completions( ) if byFuzzy != 0 then byFuzzy else - val byMethodScore = Integer.compare( - methodScore(sym1), - methodScore(sym2) - ) - if byMethodScore != 0 then byMethodScore + val byRelevance = compareByRelevance(o1, o2) + if byRelevance != 0 then byRelevance else - val byRelevance = compareByRelevance(o1, o2) - if byRelevance != 0 then byRelevance + val byMethodScore = Integer.compare( + methodScore(sym1), + methodScore(sym2) + ) + if byMethodScore != 0 then byMethodScore else val byIdentifier = IdentifierComparator.compare( s1.name.show, diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala index 61239b535e1c..f4bfc806dbb3 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionArgSuite.scala @@ -96,7 +96,7 @@ class CompletionArgSuite extends BaseCompletionSuite: """|age = : Int |followers = : Int |Main test - |User test + |User(name: String = ..., age: Int = ..., address: String = ..., followers: Int = ...): User |""".stripMargin, topLines = Option(4) ) @@ -130,7 +130,7 @@ class CompletionArgSuite extends BaseCompletionSuite: """|age = : Int |followers = : Int |Main test - |User test + |User(name: String = ..., age: Int = ..., address: String = ..., followers: Int = ...): User |""".stripMargin, topLines = Option(4) ) @@ -1119,4 +1119,4 @@ class CompletionArgSuite extends BaseCompletionSuite: |""".stripMargin, """x: Int |x = : Any""".stripMargin, - ) \ No newline at end of file + ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala index 973b780f0ccb..86a65dd9ea41 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala @@ -182,11 +182,10 @@ class CompletionDocSuite extends BaseCompletionSuite: |} """.stripMargin, """ - |> Found documentation for scala/util/Try. - |Try scala.util |> Found documentation for scala/util/Try.apply(). |Try[T](r: => T): Try[T] - |new Try[T]: Try[T] + |> Found documentation for scala/util/Try. + |Try scala.util |""".stripMargin, includeDocs = true ) @@ -200,7 +199,7 @@ class CompletionDocSuite extends BaseCompletionSuite: """.stripMargin, """ |> Found documentation for scala/collection/mutable/StringBuilder. - |StringBuilder scala.collection.mutable + |StringBuilder(): StringBuilder |""".stripMargin, includeDocs = true, topLines = Some(1) @@ -214,9 +213,9 @@ class CompletionDocSuite extends BaseCompletionSuite: |} """.stripMargin, """ + |Vector[A](elems: A*): Vector[A] |> Found documentation for scala/package.Vector. |Vector scala.collection.immutable - |Vector[A](elems: A*): Vector[A] |""".stripMargin, includeDocs = true ) @@ -247,8 +246,8 @@ class CompletionDocSuite extends BaseCompletionSuite: | scala.util.Failure@@ |} """.stripMargin, - """|Failure scala.util - |Failure[T](exception: Throwable): Failure[T] + """|Failure[T](exception: Throwable): Failure[T] + |Failure scala.util |""".stripMargin, includeDocs = true ) @@ -306,7 +305,7 @@ class CompletionDocSuite extends BaseCompletionSuite: | } |} """.stripMargin, - """|myNumbers: Vector[Int] - |myNumbers(i: Int): Int + """|myNumbers(i: Int): Int + |myNumbers: Vector[Int] |""".stripMargin ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala index 6e490ea2db01..67dfae11ffa9 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala @@ -8,6 +8,7 @@ import dotty.tools.pc.utils.MockEntries import org.junit.Test import org.junit.Ignore +import scala.collection.immutable.ListMapBuilder class CompletionExtraConstructorSuite extends BaseCompletionSuite: @@ -50,8 +51,8 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | case class TestClass(x: Int) | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int): TestClass (Method) + """|TestClass(x: Int): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -62,8 +63,8 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | case class TestClass() | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(): TestClass (Method) + """|TestClass(): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -74,56 +75,60 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | case class TestClass[T](x: T) | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass[T](x: T): TestClass[T] (Method) + """|TestClass[T](x: T): TestClass[T] (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) - @Ignore + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(x: Int): TestClass (Constructor) @Test def `extra-new-completions-abstract-class-1` = check( """|object Wrapper: | abstract class TestClass(x: Int) | TestCla@@ |""".stripMargin, - """|new TestClass(x: Int): TestClass (Constructor) + """| |""".stripMargin, includeCompletionKind = true ) - @Ignore + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(): TestClass (Constructor) @Test def `extra-new-completions-abstract-class-2` = check( """|object Wrapper: | abstract class TestClass() | TestCla@@ |""".stripMargin, - """|new TestClass(): TestClass (Constructor) + """| |""".stripMargin, includeCompletionKind = true ) - @Ignore + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass[T](x: T): TestClass[T] (Constructor) @Test def `extra-new-completions-abstract-class-3` = check( """|object Wrapper: | abstract class TestClass[T](x: T) | TestCla@@ |""".stripMargin, - """|new TestClass[T](x: T): TestClass[T] (Constructor) + """| |""".stripMargin, includeCompletionKind = true ) - @Ignore + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass (Constructor) @Test def `extra-new-completions-trait-1` = check( """|object Wrapper: | trait TestClass | TestCla@@ |""".stripMargin, - """|new TestClass (Constructor) + """| |""".stripMargin, includeCompletionKind = true ) @@ -136,9 +141,9 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(x: Int, y: Int): TestClass = TestClass(x + y) | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int, y: Int): TestClass (Method) + """|TestClass(x: Int, y: Int): TestClass (Method) |new TestClass(x: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -152,9 +157,9 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | TestCla@@ |} |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int): TestClass (Method) + """|TestClass(x: Int): TestClass (Method) |new TestClass(x: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -167,13 +172,15 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(): TestClass = TestClass(1) | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(): TestClass (Method) + """|TestClass(): TestClass (Method) |new TestClass(): TestClass (Constructor) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(x: Int): TestClass (Constructor) @Test def `extra-new-completions-abstract-class-with-companion-1` = check( """|object Wrapper: @@ -182,13 +189,14 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(x: Int, y: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int, y: Int): TestClass (Method) - |new TestClass(x: Int): TestClass (Constructor) + """|TestClass(x: Int, y: Int): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(x: Int): TestClass (Constructor) @Test def `extra-new-completions-abstract-class-with-companion-2` = check( """|object Wrapper: @@ -197,13 +205,14 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(x: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int): TestClass (Method) - |new TestClass(x: Int): TestClass (Constructor) + """|TestClass(x: Int): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(): TestClass (Constructor) @Test def `extra-new-completions-abstract-class-with-companion-3` = check( """|object Wrapper: @@ -212,13 +221,14 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(): TestClass (Method) - |new TestClass(): TestClass (Constructor) + """|TestClass(): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(x: Int): TestClass (Constructor) @Test def `extra-new-completions-trait-with-companion-1` = check( """|object Wrapper: @@ -227,13 +237,14 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(x: Int, y: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int, y: Int): TestClass (Method) - |new TestClass(x: Int): TestClass (Constructor) + """|TestClass(x: Int, y: Int): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(x: Int): TestClass (Constructor) @Test def `extra-new-completions-trait-with-companion-2` = check( """|object Wrapper: @@ -242,13 +253,14 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(x: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int): TestClass (Method) - |new TestClass(x: Int): TestClass (Constructor) + """|TestClass(x: Int): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(): TestClass (Constructor) @Test def `extra-new-completions-trait-with-companion-3` = check( """|object Wrapper: @@ -257,14 +269,15 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(): TestClass (Method) - |new TestClass(): TestClass (Constructor) + """|TestClass(): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) // This test should have new TestClass completion without parentheses. The actual issue is with printer, edit text is correct + // TODO We first need to detect support when to add additional braces / colon + // missing new TestClass(): TestClass (Constructor) @Test def `extra-new-completions-trait-with-companion-4` = check( """|object Wrapper: @@ -273,9 +286,8 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(): TestClass (Method) - |new TestClass(): TestClass (Constructor) + """|TestClass(): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -286,9 +298,8 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass - |TestClass() - |new TestClass + """|TestClass() + |TestClass |""".stripMargin, ) @@ -330,11 +341,11 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(z: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(z: Int): TestClass (Method) + """|TestClass(z: Int): TestClass (Method) |new TestClass(): TestClass (Constructor) |new TestClass(x: Int): TestClass (Constructor) |new TestClass(x: Int, y: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -350,12 +361,12 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(z: Int, w: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(z: Int): TestClass (Method) + """|TestClass(z: Int): TestClass (Method) |TestClass(z: Int, w: Int): TestClass (Method) |new TestClass(): TestClass (Constructor) |new TestClass(x: Int): TestClass (Constructor) |new TestClass(x: Int, y: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -370,11 +381,11 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(x: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int): TestClass (Method) + """|TestClass(x: Int): TestClass (Method) |new TestClass(): TestClass (Constructor) |new TestClass(x: Int): TestClass (Constructor) |new TestClass(x: Int, y: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -389,11 +400,11 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(x: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) + """|TestClass(): TestClass (Method) |TestClass(x: Int): TestClass (Method) - |TestClass(): TestClass (Method) |new TestClass(x: Int): TestClass (Constructor) |new TestClass(x: Int, y: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -401,22 +412,23 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: @Test def `multiple-extra-new-constructors-with-companion-same-signature-trait` = check( """|object Wrapper: - | trait TestClass: - | def this(x: Int) = this() - | def this(x: Int, y: Int) = this() + | trait TestClass | object TestClass: | def apply(x: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int): TestClass (Method) - |new TestClass(): TestClass (Constructor) - |new TestClass(x: Int): TestClass (Constructor) - |new TestClass(x: Int, y: Int): TestClass (Constructor) + """|TestClass(x: Int): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) + + // TODO We first need to detect support when to add additional braces / colon + // missing: + // new TestClass(): TestClass (Constructor) + // new TestClass(x: Int): TestClass (Constructor) + // new TestClass(x: Int, y: Int): TestClass (Constructor) @Test def `multiple-extra-new-constructors-with-companion-same-signature-abstract` = check( """|object Wrapper: @@ -427,11 +439,8 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | def apply(x: Int): TestClass = ??? | TestCla@@ |""".stripMargin, - """|TestClass test.Wrapper (Module) - |TestClass(x: Int): TestClass (Method) - |new TestClass(): TestClass (Constructor) - |new TestClass(x: Int): TestClass (Constructor) - |new TestClass(x: Int, y: Int): TestClass (Constructor) + """|TestClass(x: Int): TestClass (Method) + |TestClass test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -502,12 +511,11 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: | TestCla@@ |} |""".stripMargin, - """| - |TestClass - test.Wrapper (Module) - |TestClass(x: Int): TestClass - test.Wrapper (Method) + """|TestClass(x: Int): TestClass - test.Wrapper (Method) |new TestClass(): TestClass - test.Wrapper (Constructor) |new TestClass(x: Int): TestClass - test.Wrapper (Constructor) |new TestClass(x: Int, y: Int): TestClass - test.Wrapper (Constructor) + |TestClass - test.Wrapper (Module) |""".stripMargin, includeCompletionKind = true ) @@ -515,24 +523,32 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: @Test def `prepend-new` = checkSnippet( """|object Wrapper: - | Try@@ - | - |""".stripMargin, - """|Try - |Try($0) - |new Try + | case class TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + |object Main { + | TestClas@@ + |} |""".stripMargin, + """|TestClass($0) + |new TestClass + |TestClass + |""".stripMargin ) @Test def `prepend-new-fully-qualified-path` = checkSnippet( """|object Wrapper: - | scala.util.Try@@ - | - |""".stripMargin, - """|Try - |Try($0) - |new scala.util.Try + | case class TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + |object Main { + | Wrapper.Test@@ + |} |""".stripMargin, + """|TestClass($0) + |new Wrapper.TestClass + |TestClass + |""".stripMargin ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala index eaaf0ed343c9..08cc1535fd56 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionInterpolatorSuite.scala @@ -4,6 +4,7 @@ import dotty.tools.pc.base.BaseCompletionSuite import org.junit.runners.MethodSorters import org.junit.{FixMethodOrder, Test} +import org.junit.Ignore @FixMethodOrder(MethodSorters.NAME_ASCENDING) class CompletionInterpolatorSuite extends BaseCompletionSuite: @@ -627,6 +628,7 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: |} |""".stripMargin, assertSingleItem = false, + filter = _.contains("java.nio.file") ) @@ -744,7 +746,6 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: | val a = s"${ListBuffer($0)}"" |}""".stripMargin, assertSingleItem = false, - itemIndex = 1 ) @Test def `dont-show-when-writing-before-dollar` = @@ -781,25 +782,60 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: ) @Test def `prepend-new-missing-interpolator` = + checkSnippet( + """|case class TestClass(x: Int) + |object TestClass: + | def apply(x: Int): TestClass = ??? + |object Main: + | "$TestClas@@" + |""".stripMargin, + """|{TestClass($0)} + |{new TestClass$0} + |TestClass$0 + |""".stripMargin + ) + + @Ignore("This case is not yet supported by metals") + @Test def `prepend-new-missing-interpolator-with-prefix` = checkSnippet( """|object Wrapper: - | "$Try@@" - | - |""".stripMargin, - """|Try$0 - |{Try($0)} - |{new Try$0} - |""".stripMargin, + | case class TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + |object Main: + | "$Wrapper.TestClas@@" + |""".stripMargin, + """|{Wrapper.TestClass($0)} + |{new Wrapper.TestClass$0} + |{Wrapper.TestClass$0} + |""".stripMargin ) - @Test def `prepend-new-interpolator` = + @Test def `prepend-new-with-prefix` = checkSnippet( """|object Wrapper: - | s"$Try@@" - | - |""".stripMargin, - """|Try - |{Try($0)} - |{new Try} - |""".stripMargin, + | case class TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + |object Main: + | s"$Wrapper.TestClas@@" + |""".stripMargin, + """|{Wrapper.TestClass($0)} + |{new Wrapper.TestClass$0} + |{Wrapper.TestClass$0} + |""".stripMargin + ) + + @Test def `prepend-new-interpolator` = + checkSnippet( + """|case class TestClass(x: Int) + |object TestClass: + | def apply(x: Int): TestClass = ??? + |object Main: + | s"$TestClas@@" + |""".stripMargin, + """|{TestClass($0)} + |{new TestClass} + |TestClass + |""".stripMargin ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala index f28a014fe5ee..b601a63ff234 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala @@ -304,25 +304,33 @@ class CompletionSnippetSuite extends BaseCompletionSuite: @Test def `case-class2` = checkSnippet( - s"""|object Main { - | scala.util.Tr@@ + s"""|object wrapper: + | case class Test2(x: Int) + | object Test2: + | def apply(x: Int): Test2 = ??? + |object Main { + | wrapper.Test@@ |} |""".stripMargin, - """|Try - |Try($0) - |new scala.util.Try + """|Test2($0) + |new wrapper.Test2 + |Test2 |""".stripMargin ) @Test def `case-class2-edit` = checkEditLine( - s"""|object Main { + s"""|object wrapper: + | case class Test2(x: Int) + | object Test2: + | def apply(x: Int): Test2 = ??? + |object Main { | ___ |} |""".stripMargin, - "scala.util.Tr@@", - "new scala.util.Try", - filter = _.contains("new Try") + "wrapper.Test@@", + "new wrapper.Test2", + filter = _.contains("new Test2") ) @Test def `case-class3` = @@ -333,9 +341,8 @@ class CompletionSnippetSuite extends BaseCompletionSuite: |""".stripMargin, // Note: the class and trait items in here are invalid. So // they are filtered out. - """|Try - scala.util - |Try($0) - [T](r: => T): Try[T] - |new Try - [T]: Try[T] + """|Try($0) - [T](r: => T): Try[T] + |Try - scala.util |""".stripMargin, includeDetail = true ) @@ -365,10 +372,10 @@ class CompletionSnippetSuite extends BaseCompletionSuite: | Wi@@ |} |""".stripMargin, - """|Widget - example - |Widget($0) - (name: String): Widget + """|Widget($0) - (name: String): Widget |Widget($0) - (age: Int): Widget |Widget($0) - (name: String, age: Int): Widget + |Widget - example |""".stripMargin, includeDetail = true, topLines = Some(4) @@ -396,12 +403,13 @@ class CompletionSnippetSuite extends BaseCompletionSuite: | ListMa@@ |} |""".stripMargin, - """|ListMap - scala.collection.immutable - |ListMap - scala.collection.mutable + """|ListMap($0) - [K, V](elems: (K, V)*): ListMap[K, V] + |new ListMap - [K, V]: ListMap[K, V] + |ListMap - scala.collection.immutable |ListMap($0) - [K, V](elems: (K, V)*): ListMap[K, V] |new ListMap - [K, V]: ListMap[K, V] + |ListMap - scala.collection.mutable |ListMapBuilder - [K, V]: ListMapBuilder[K, V] - |new ListMap - [K, V]: ListMap[K, V] |ConcurrentSkipListMap - java.util.concurrent |""".stripMargin, includeDetail = true, diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index 3ccf26643bb9..38b689ff6bdb 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -26,8 +26,8 @@ class CompletionSuite extends BaseCompletionSuite: | Lis@@ |}""".stripMargin, """ - |List scala.collection.immutable |List[A](elems: A*): List[A] + |List scala.collection.immutable |List - java.awt |List - java.util |""".stripMargin, @@ -178,10 +178,10 @@ class CompletionSuite extends BaseCompletionSuite: |object A { | TrieMap@@ |}""".stripMargin, - """|TrieMap scala.collection.concurrent - |TrieMap[K, V](elems: (K, V)*): TrieMap[K, V] + """|TrieMap[K, V](elems: (K, V)*): TrieMap[K, V] |new TrieMap[K, V]: TrieMap[K, V] |new TrieMap[K, V](hashf: Hashing[K], ef: Equiv[K]): TrieMap[K, V] + |TrieMap scala.collection.concurrent |""".stripMargin ) @@ -629,8 +629,8 @@ class CompletionSuite extends BaseCompletionSuite: |} |""".stripMargin, """|Some(value) scala - |Some scala |Some[A](value: A): Some[A] + |Some scala |""".stripMargin ) @@ -641,8 +641,8 @@ class CompletionSuite extends BaseCompletionSuite: | case List(Som@@) |} |""".stripMargin, - """|Some scala - |Some[A](value: A): Some[A] + """|Some[A](value: A): Some[A] + |Some scala |""".stripMargin ) @@ -667,8 +667,8 @@ class CompletionSuite extends BaseCompletionSuite: |} |""".stripMargin, """|Some(value) scala - |Seq scala.collection.immutable - |Set scala.collection.immutable + |Set[A](elems: A*): Set[A] + |Seq[A](elems: A*): Seq[A] |""".stripMargin, topLines = Some(3) ) @@ -1627,8 +1627,8 @@ class CompletionSuite extends BaseCompletionSuite: | val fooBar = List(123) | foo@@ |""".stripMargin, - """|fooBar: List[Int] - |fooBar(n: Int): Int + """|fooBar(n: Int): Int + |fooBar: List[Int] |""".stripMargin ) @@ -1638,9 +1638,11 @@ class CompletionSuite extends BaseCompletionSuite: | List@@ |""".stripMargin, """|List[A](elems: A*): List[A] + |ListSet[A](elems: A*): ListSet[A] - scala.collection.immutable |ListMap[K, V](elems: (K, V)*): ListMap[K, V] - scala.collection.immutable |new ListMap[K, V]: ListMap[K, V] - scala.collection.immutable |new ListSet[A]: ListSet[A] - scala.collection.immutable + |ListMap[K, V](elems: (K, V)*): ListMap[K, V] - scala.collection.mutable |new ListMap[K, V]: ListMap[K, V] - scala.collection.mutable |""".stripMargin, filter = _.contains("[") diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala index cbc46f207634..4cae717a7d26 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala @@ -810,8 +810,8 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: |} |""".stripMargin, """|fooBar: String - |fooBar: List[Int] |fooBar(n: Int): Int - test.A + |fooBar: List[Int] |""".stripMargin, ) @@ -827,8 +827,8 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: | |val j = MyTy@@ |""".stripMargin, - """|MyType - demo.other - |MyType(m: Long): MyType - demo.other + """|MyType(m: Long): MyType - demo.other + |MyType - demo.other """.stripMargin, ) @@ -844,8 +844,8 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: | |val j = MyTy@@ |""".stripMargin, - """|MyType - demo.other - |MyType(m: Long): MyType - demo.other + """|MyType(m: Long): MyType - demo.other + |MyType - demo.other """.stripMargin, ) From 8414dd9e65752771e8325af9b49ce2d97f1c7c0b Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 11 Apr 2024 16:28:31 +0200 Subject: [PATCH 08/10] Add tests that verify private members are filtered --- .../tools/pc/completions/CompletionProvider.scala | 1 - .../CompletionExtraConstructorSuite.scala | 13 +++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala index 6cfd87773c8e..7e02c23229e8 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -32,7 +32,6 @@ import org.eclipse.lsp4j.InsertTextFormat import org.eclipse.lsp4j.InsertTextMode import org.eclipse.lsp4j.Range as LspRange import org.eclipse.lsp4j.TextEdit -import dotty.tools.dotc.cc.CaptureSet.empty class CompletionProvider( search: SymbolSearch, diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala index 67dfae11ffa9..7aa7380b7f1a 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala @@ -552,3 +552,16 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: |""".stripMargin ) + @Test def `dont-include-private-members` = + check( + """|object TestObject: + | private def apply(i: Int) = i + |object Main: + | TestObject@@ + |""".stripMargin, + """|TestClass($0) + |new Wrapper.TestClass + |TestClass + |""".stripMargin + ) + From adde393cdc39792d5904d82da335aea95d46a907 Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 11 Apr 2024 16:36:34 +0200 Subject: [PATCH 09/10] Adjust test --- .../pc/tests/completion/CompletionExtraConstructorSuite.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala index 7aa7380b7f1a..010d0b14fa90 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala @@ -559,9 +559,7 @@ class CompletionExtraConstructorSuite extends BaseCompletionSuite: |object Main: | TestObject@@ |""".stripMargin, - """|TestClass($0) - |new Wrapper.TestClass - |TestClass + """|TestObject test |""".stripMargin ) From bc7c848522c97f4309742c98e77940cd718aaa5e Mon Sep 17 00:00:00 2001 From: rochala Date: Fri, 12 Apr 2024 11:04:32 +0200 Subject: [PATCH 10/10] Don't show extra applies for non-type-forwarder vals --- .../src/main/dotty/tools/pc/completions/Completions.scala | 4 +++- .../dotty/tools/pc/tests/completion/CompletionDocSuite.scala | 3 +-- .../dotty/tools/pc/tests/completion/CompletionSuite.scala | 3 +-- .../tools/pc/tests/completion/CompletionWorkspaceSuite.scala | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) 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 3bb869782e14..7a10c9e4804d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -238,7 +238,9 @@ class Completions( .filter(_.symbol.isAccessibleFrom(denot.info)) constructors -> true - else if shouldAddSnippet && completionMode.is(Mode.Term) && sym.name.isTermName && !sym.is(Flags.Method) && !sym.is(Flags.JavaDefined) then + else if shouldAddSnippet && completionMode.is(Mode.Term) && sym.name.isTermName && + !sym.is(Flags.JavaDefined) && (sym.isClass || sym.is(Module) || (sym.isField && denot.info.isInstanceOf[TermRef])) then + val constructors = if sym.isAllOf(ConstructorProxyModule) then sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols else diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala index 86a65dd9ea41..b487611b9ea1 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala @@ -305,7 +305,6 @@ class CompletionDocSuite extends BaseCompletionSuite: | } |} """.stripMargin, - """|myNumbers(i: Int): Int - |myNumbers: Vector[Int] + """|myNumbers: Vector[Int] |""".stripMargin ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala index 38b689ff6bdb..ebca80dc0717 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -1627,8 +1627,7 @@ class CompletionSuite extends BaseCompletionSuite: | val fooBar = List(123) | foo@@ |""".stripMargin, - """|fooBar(n: Int): Int - |fooBar: List[Int] + """|fooBar: List[Int] |""".stripMargin ) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala index 4cae717a7d26..c8cfbd178f32 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionWorkspaceSuite.scala @@ -810,7 +810,6 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: |} |""".stripMargin, """|fooBar: String - |fooBar(n: Int): Int - test.A |fooBar: List[Int] |""".stripMargin, )