diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 025a2022500d..44407daf600c 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 @@ -130,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 @@ -278,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) @@ -531,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 d0ceb37c07ba..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: 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/CompletionAffix.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala new file mode 100644 index 000000000000..4ed58c773a7c --- /dev/null +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionAffix.scala @@ -0,0 +1,95 @@ +package dotty.tools.pc.completions + +import org.eclipse.lsp4j.Position +import org.eclipse.lsp4j.Range + +/** + * @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[Suffix], + prefixes: Set[Prefix], + snippet: Suffix, + currentPrefix: Option[String], +): + def addLabelSnippet = suffixes.exists(_.kind == SuffixKind.Bracket) + def hasSnippet = snippet.kind != SuffixKind.NoSuffix + def chain(copyFn: CompletionAffix => CompletionAffix) = copyFn(this) + 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.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.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[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 Range(startPos, endPos) + + private def loopPrefix(prefixes: List[PrefixKind]): String = + prefixes match + case PrefixKind.New :: tail => "new " + loopPrefix(tail) + 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 = + loopPrefix(prefixes.toList.map(_.kind)) + +end CompletionAffix + +object CompletionAffix: + val empty = CompletionAffix( + suffixes = Set.empty, + prefixes = Set.empty, + snippet = Affix(SuffixKind.NoSuffix), + currentPrefix = None, + ) + +enum SuffixKind: + case Brace, Bracket, Template, NoSuffix + +enum PrefixKind: + case New + +type Suffix = Affix[SuffixKind] +type Prefix = Affix[PrefixKind] + +private case class Affix[+T](kind: T, insertRange: Option[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 2beb4460db56..7e02c23229e8 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionProvider.scala @@ -153,13 +153,36 @@ 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 + // 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 underlyingCompletion.snippetAffix.nonEmpty && isInStringInterpolation then + "{" + newText + "}" + else newText def mkItem( newText: String, @@ -170,25 +193,25 @@ 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") 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)) } @@ -196,21 +219,8 @@ class CompletionProvider( item end mkItem - val completionTextSuffix = completion.snippetSuffix.toEdit - - 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 + val completionTextSuffix = underlyingCompletion.snippetAffix.toSuffix + val completionTextPrefix = underlyingCompletion.snippetAffix.toInsertPrefix lazy val backtickSoftKeyword = path match case (_: Select) :: _ => false @@ -232,7 +242,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,25 +252,25 @@ class CompletionProvider( case IndexedContext.Result.InScope => mkItem( v.insertText.getOrElse( - 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( - "{" + 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 @@ -270,18 +280,16 @@ 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 insert = - completion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) - mkItem( - insert + completionTextSuffix, - range = completion.range - ) + val nameText = underlyingCompletion.insertText.getOrElse(ident.backticked(backtickSoftKeyword)) + val nameWithAffixes = completionTextPrefix + nameText + completionTextSuffix + mkItem(nameWithAffixes, range = underlyingCompletion.range) + end match end completionItems end CompletionProvider diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionSuffix.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionSuffix.scala deleted file mode 100644 index 580d65089737..000000000000 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/CompletionSuffix.scala +++ /dev/null @@ -1,39 +0,0 @@ -package dotty.tools.pc.completions - -/** - * @param suffixes which we should insert - * @param snippet which suffix should we insert the snippet $0 - */ -case class CompletionSuffix( - suffixes: Set[SuffixKind], - 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 withNewSuffixSnippet(kind: SuffixKind) = - CompletionSuffix(suffixes + kind, kind) - def toEdit: String = - def loop(suffixes: List[SuffixKind]): String = - def cursor = if suffixes.head == snippet 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) - def toEditOpt: Option[String] = - val edit = toEdit - if edit.nonEmpty then Some(edit) else None -end CompletionSuffix - -object CompletionSuffix: - val empty = CompletionSuffix( - suffixes = Set.empty, - snippet = SuffixKind.NoSuffix, - ) - -enum SuffixKind: - case Brace, Bracket, Template, NoSuffix 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..e1877a1a9c88 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 snippetAffix: 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 @@ -80,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 @@ -97,20 +99,18 @@ 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 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 - 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)}" 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) @@ -126,29 +126,64 @@ object CompletionValue: case class Compiler( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix + override val snippetAffix: CompletionAffix ) 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, - override val snippetSuffix: CompletionSuffix, + override val snippetAffix: CompletionAffix, ) extends Symbolic: override def completionItemDataKind: Integer = CompletionSource.ScopeKind.ordinal case class Workspace( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix, + override val snippetAffix: 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.isConstructor || symbol.name == nme.apply then + 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 + s"${labelWithSuffix(printer)} -${description(printer)}" else super.labelWithDescription(printer) /** @@ -157,7 +192,7 @@ object CompletionValue: case class ImplicitClass( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix, + override val snippetAffix: CompletionAffix, override val importSymbol: Symbol, ) extends Symbolic: override def completionItemKind(using Context): CompletionItemKind = @@ -172,7 +207,7 @@ object CompletionValue: case class Extension( label: String, denotation: Denotation, - override val snippetSuffix: CompletionSuffix + override val snippetAffix: CompletionAffix ) extends Symbolic: override def completionItemKind(using Context): CompletionItemKind = CompletionItemKind.Method @@ -257,6 +292,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 81a543701817..7a10c9e4804d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -8,12 +8,11 @@ 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.* 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,7 +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 dotty.tools.dotc.interactive.Interactive + class Completions( text: String, @@ -69,10 +68,9 @@ class Completions( false case (_: (Import | Export)) :: _ => false case _ :: (_: (Import | Export)) :: _ => false - 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 @@ -88,7 +86,6 @@ class Completions( val generalExclude = isUninterestingSymbol(sym) || !isNotLocalForwardReference(sym) || - sym.isPackageObject || hasSyntheticCursorSuffix def isWildcardParam(sym: Symbol) = @@ -105,14 +102,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 => @@ -120,18 +126,10 @@ 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) = 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 @@ -147,7 +145,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) @@ -157,13 +155,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} @@ -184,20 +186,19 @@ 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(Affix(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 - 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,58 +206,93 @@ 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 && allowTemplateSuffix - && isAbstractType(symbol) - then - if suffix.hasSnippet then suffix.withNewSuffix(SuffixKind.Template) - else suffix.withNewSuffixSnippet(SuffixKind.Template) + if shouldAddSnippet && isNew && isAbstractType(symbol) then + 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, CompletionSuffix) => CompletionValue + toCompletionValue: (String, SingleDenotation, CompletionAffix) => CompletionValue.Symbolic ): 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) && - (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 - - methodDenots.map { methodDenot => - val suffix = findSuffix(methodDenot.symbol) + val hasNonSyntheticConstructor = sym.name.isTypeName && sym.isClass + && !sym.is(ModuleClass) && !sym.is(Trait) && !sym.is(Abstract) && !sym.is(Flags.JavaDefined) + + val (extraMethodDenots, skipOriginalDenot): (List[SingleDenotation], Boolean) = + if shouldAddSnippet && isNew && hasNonSyntheticConstructor then + 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.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 + val companionApplies = denot.info.member(nme.apply).allSymbols + val classConstructors = if sym.companionClass.exists && !sym.companionClass.isOneOf(AbstractOrTrait) then + sym.companionClass.info.member(nme.CONSTRUCTOR).allSymbols + else Nil + + if companionApplies.exists(_.is(Synthetic)) then + companionApplies ++ classConstructors.filter(!_.isPrimaryConstructor) + else + companionApplies ++ classConstructors + + val result = constructors.map(_.asSeenFrom(denot.info).asSingleDenotation) + .filter(_.symbol.isAccessibleFrom(denot.info)) + + result -> (sym.isAllOf(ConstructorProxyModule) || sym.is(Trait)) + else Nil -> false + + val extraCompletionValues = + val existsApply = extraMethodDenots.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 + + suffix + .withCurrentPrefix(qual.show + ".") + .withNewPrefix(Affix(PrefixKind.New, insertRange = Some(insertRange))) + case _ => + suffix.withNewPrefix(Affix(PrefixKind.New)) + else suffix + val name = undoBacktick(label) + + 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, - suffix - ) - } - end completionsWithSuffix + val denotCompletionValue = toCompletionValue(name, denot, suffix) + denotCompletionValue :: extraCompletionValues + + end completionsWithAffix /** * @return Tuple of completionValues and flag. If the latter boolean value is true @@ -495,13 +531,22 @@ 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, + snippetAffix = CompletionAffix.empty, + importSymbol = sym + ) + ) case _ => - completionsWithSuffix( + completionsWithAffix( sym, sym.decodedName, CompletionValue.Workspace(_, _, _, sym) @@ -534,13 +579,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), @@ -569,13 +614,36 @@ 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 (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 + + val filteredSymbolicCompletions = symbolicCompletionsMap.filter: (name, denots) => + lazy val existsTypeWithoutSuffix: Boolean = !symbolicCompletionsMap + .get(name.toTypeName) + .forall(_.forall(sym => sym.snippetAffix.suffixes.nonEmpty)) + + (completionMode.is(Mode.Term) && !completionMode.is(Mode.ImportOrExport)) || + // show non synthetic symbols + // companion test should not result TrieMap[K, V] + (name.isTermName && !existsTypeWithoutSuffix) || + name.isTypeName + .toList.unzip._2.flatten + + filteredSymbolicCompletions ++ 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) = @@ -585,15 +653,13 @@ class Completions( case ck: CompletionValue.CaseKeyword => (ck.label, true) 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 name = symOnly match + case CompletionValue.ExtraMethod(owner, extraMethod) => + SemanticdbSymbols.symbolName(owner.symbol) + SemanticdbSymbols.symbolName(extraMethod.symbol) + case _ => SemanticdbSymbols.symbolName(sym) val suffix = - if symOnly.snippetSuffix.addLabelSnippet then "[]" else "" - val id = nameId + suffix + if symOnly.snippetAffix.addLabelSnippet then "[]" else "" + val id = name + suffix val include = includeSymbol(sym) (id, include) case kw: CompletionValue.Keyword => (kw.label, true) @@ -604,8 +670,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 +681,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 @@ -704,18 +767,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]( @@ -823,6 +892,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) @@ -862,6 +932,23 @@ class Completions( prioritizeCaseKeyword || prioritizeNamed end compareCompletionValue + def methodScore(v: CompletionValue.Symbolic)(using Context): Int = + val sym = v.symbol + 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 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 = (o1, o2) match case (o1: CompletionValue.NamedArg, o2: CompletionValue.NamedArg) => @@ -881,32 +968,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) - ) - if byFuzzy != 0 then byFuzzy + val byRelevance = compareByRelevance(o1, o2) + if byRelevance != 0 then byRelevance else - val byIdentifier = IdentifierComparator.compare( - s1.name.show, - s2.name.show + val byMethodScore = Integer.compare( + methodScore(sym1), + methodScore(sym2) ) - if byIdentifier != 0 then byIdentifier + if byMethodScore != 0 then byMethodScore 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/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/InterpolatorCompletions.scala index 2a8ead70ea33..9c973e6e63e0 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.* @@ -112,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 @@ -155,14 +152,14 @@ object InterpolatorCompletions: sym.name.toString() ) => val label = sym.name.decoded - completions.completionsWithSuffix( + completions.completionsWithAffix( sym, label, - (name, denot, suffix) => + (name, denot, affix) => CompletionValue.Interpolator( denot.symbol, label, - Some(newText(name, suffix.toEditOpt, 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 @@ -252,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 @@ -286,14 +285,14 @@ object InterpolatorCompletions: sym.name.decoded ) && !sym.isType => val label = sym.name.decoded - completions.completionsWithSuffix( + completions.completionsWithAffix( sym, label, - (name, denot, suffix) => + (name, denot, affix) => CompletionValue.Interpolator( denot.symbol, label, - Some(newText(name, suffix.toEditOpt)), + Some(newText(name, affix)), additionalEdits(), Some(nameRange), None, 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 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/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 45f07b5fb7b1..b487611b9ea1 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala @@ -182,10 +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] + |> Found documentation for scala/util/Try. + |Try scala.util |""".stripMargin, includeDocs = true ) @@ -199,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) @@ -213,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 ) @@ -228,11 +228,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 @@ -249,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 ) @@ -264,16 +261,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 ) @@ -317,6 +306,5 @@ class CompletionDocSuite extends BaseCompletionSuite: |} """.stripMargin, """|myNumbers: Vector[Int] - |myNumbers(i: Int): 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 new file mode 100644 index 000000000000..010d0b14fa90 --- /dev/null +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtraConstructorSuite.scala @@ -0,0 +1,565 @@ +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 +import scala.collection.immutable.ListMapBuilder + +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(x: Int): TestClass (Method) + |TestClass test.Wrapper (Module) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-new-completions-case-class-2` = + check( + """|object Wrapper: + | case class TestClass() + | TestCla@@ + |""".stripMargin, + """|TestClass(): TestClass (Method) + |TestClass test.Wrapper (Module) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `no-extra-new-completions-case-class-3` = + check( + """|object Wrapper: + | case class TestClass[T](x: T) + | TestCla@@ + |""".stripMargin, + """|TestClass[T](x: T): TestClass[T] (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-1` = + check( + """|object Wrapper: + | abstract class TestClass(x: Int) + | TestCla@@ + |""".stripMargin, + """| + |""".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-2` = + check( + """|object Wrapper: + | abstract class TestClass() + | TestCla@@ + |""".stripMargin, + """| + |""".stripMargin, + includeCompletionKind = true + ) + + // 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, + """| + |""".stripMargin, + includeCompletionKind = true + ) + + // 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, + """| + |""".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(x: Int, y: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) + |""".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(x: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) + |""".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(): 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: + | abstract class TestClass(x: Int) + | object TestClass: + | def apply(x: Int, y: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|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: + | abstract class TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|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: + | abstract class TestClass() + | object TestClass: + | def apply(): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|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: + | trait TestClass(x: Int) + | object TestClass: + | def apply(x: Int, y: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|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: + | trait TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|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: + | trait TestClass() + | object TestClass: + | def apply(): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|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: + | trait TestClass + | object TestClass: + | def apply(): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass(): TestClass (Method) + |TestClass test.Wrapper (Module) + |""".stripMargin, + includeCompletionKind = true + ) + checkSnippet( + """|object Wrapper: + | trait TestClass + | object TestClass: + | def apply(): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|TestClass() + |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(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 + ) + + @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(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 + ) + + @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(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 + ) + + @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(): TestClass (Method) + |TestClass(x: Int): TestClass (Method) + |new TestClass(x: Int): TestClass (Constructor) + |new TestClass(x: Int, y: Int): TestClass (Constructor) + |TestClass test.Wrapper (Module) + |""".stripMargin, + includeCompletionKind = true + ) + + @Test def `multiple-extra-new-constructors-with-companion-same-signature-trait` = + check( + """|object Wrapper: + | trait TestClass + | object TestClass: + | def apply(x: Int): TestClass = ??? + | TestCla@@ + |""".stripMargin, + """|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: + | 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(x: Int): TestClass (Method) + |TestClass test.Wrapper (Module) + |""".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(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 + ) + + @Test def `prepend-new` = + checkSnippet( + """|object Wrapper: + | 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: + | case class TestClass(x: Int) + | object TestClass: + | def apply(x: Int): TestClass = ??? + |object Main { + | Wrapper.Test@@ + |} + |""".stripMargin, + """|TestClass($0) + |new Wrapper.TestClass + |TestClass + |""".stripMargin + ) + + @Test def `dont-include-private-members` = + check( + """|object TestObject: + | private def apply(i: Int) = i + |object Main: + | TestObject@@ + |""".stripMargin, + """|TestObject test + |""".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 d9dc635ce21a..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: @@ -542,7 +543,7 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: |} |""".stripMargin, """s"Hello $hello@@"""".stripMargin, - """s"Hello $helloMethod"""".stripMargin, + """s"Hello ${helloMethod($0)}"""".stripMargin, filter = _.contains("a: Int") ) @@ -627,10 +628,10 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: |} |""".stripMargin, assertSingleItem = false, - // Scala 3 has an additional Paths() completion - itemIndex = 2 + filter = _.contains("java.nio.file") ) + @Test def `auto-imports-prefix-with-interpolator` = checkEdit( """| @@ -644,7 +645,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 +745,7 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: |object Main { | val a = s"${ListBuffer($0)}"" |}""".stripMargin, - filter = _.contains("[A]") + assertSingleItem = false, ) @Test def `dont-show-when-writing-before-dollar` = @@ -780,3 +780,62 @@ class CompletionInterpolatorSuite extends BaseCompletionSuite: |""".stripMargin, "host: String" ) + + @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: + | 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-with-prefix` = + checkSnippet( + """|object Wrapper: + | 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/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..b601a63ff234 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 ) @@ -305,15 +304,35 @@ 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) + """|Test2($0) + |new wrapper.Test2 + |Test2 |""".stripMargin ) + @Test def `case-class2-edit` = + checkEditLine( + s"""|object wrapper: + | case class Test2(x: Int) + | object Test2: + | def apply(x: Int): Test2 = ??? + |object Main { + | ___ + |} + |""".stripMargin, + "wrapper.Test@@", + "new wrapper.Test2", + filter = _.contains("new Test2") + ) + @Test def `case-class3` = checkSnippet( s"""|object Main { @@ -322,9 +341,10 @@ 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($0) - [T](r: => T): Try[T] + |Try - scala.util + |""".stripMargin, + includeDetail = true ) @Test def `symbol` = @@ -352,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) @@ -365,18 +385,34 @@ 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($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] + |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..ebca80dc0717 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -26,13 +26,12 @@ 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 - |ListMap[K, V](elems: (K, V)*): ListMap[K, V] |""".stripMargin, - topLines = Some(5) + topLines = Some(4) ) @Test def member = @@ -179,8 +178,24 @@ 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 + ) + + @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 = @@ -618,8 +629,8 @@ class CompletionSuite extends BaseCompletionSuite: |} |""".stripMargin, """|Some(value) scala - |Some scala |Some[A](value: A): Some[A] + |Some scala |""".stripMargin ) @@ -630,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 ) @@ -656,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) ) @@ -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) ) @@ -1719,7 +1628,6 @@ class CompletionSuite extends BaseCompletionSuite: | foo@@ |""".stripMargin, """|fooBar: List[Int] - |fooBar(n: Int): Int |""".stripMargin ) @@ -1729,7 +1637,12 @@ class CompletionSuite extends BaseCompletionSuite: | List@@ |""".stripMargin, """|List[A](elems: A*): List[A] - |ListMap[K, V](elems: (K, V)*): ListMap[K, V] + |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 52e565a5a78b..c8cfbd178f32 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,6 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: |""".stripMargin, """|fooBar: String |fooBar: List[Int] - |fooBar(n: Int): Int |""".stripMargin, ) @@ -827,8 +826,9 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: | |val j = MyTy@@ |""".stripMargin, - """|MyType(m: Long): MyType - |MyType - demo.other""".stripMargin, + """|MyType(m: Long): MyType - demo.other + |MyType - demo.other + """.stripMargin, ) @Test def `type-apply2` = @@ -843,8 +843,9 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite: | |val j = MyTy@@ |""".stripMargin, - """|MyType(m: Long): MyType - |MyType - demo.other""".stripMargin, + """|MyType(m: Long): MyType - demo.other + |MyType - demo.other + """.stripMargin, ) @Test def `method-name-conflict` =