From 446b98faf4614835e54e301ec75378e53d154b0b Mon Sep 17 00:00:00 2001 From: rochala Date: Thu, 29 Feb 2024 11:20:23 +0100 Subject: [PATCH 1/6] Initial fuzzy search --- .../tools/dotc/interactive/Completion.scala | 24 ++++--- .../tools/pc/completions/Completions.scala | 8 ++- .../pc/completions/KeywordsCompletions.scala | 2 +- .../completion/CompletionKeywordSuite.scala | 64 ++++++++++++++++++- 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 44407daf600c..b1b8b1dcc98a 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -71,10 +71,11 @@ object Completion: mode: Mode, rawPrefix: String, tpdPath: List[tpd.Tree], - untpdPath: List[untpd.Tree] + untpdPath: List[untpd.Tree], + prefixFilter: Option[Name => Boolean] = None )(using Context): CompletionMap = val adjustedPath = typeCheckExtensionConstructPath(untpdPath, tpdPath, pos) - computeCompletions(pos, mode, rawPrefix, adjustedPath) + computeCompletions(pos, mode, rawPrefix, adjustedPath, prefixFilter) /** * Inspect `path` to determine what kinds of symbols should be considered. @@ -193,11 +194,12 @@ object Completion: .flatten.getOrElse(tpdPath) private def computeCompletions( - pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree] + pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree], prefixFilter: Option[Name => Boolean] )(using Context): CompletionMap = val hasBackTick = rawPrefix.headOption.contains('`') val prefix = if hasBackTick then rawPrefix.drop(1) else rawPrefix - val completer = new Completer(mode, prefix, pos) + val prefixFilter0 = prefixFilter.getOrElse(_.startsWith(prefix)) + val completer = new Completer(mode, prefix, pos, prefixFilter0) val result = adjustedPath match // Ignore synthetic select from `This` because in code it was `Ident` @@ -317,7 +319,7 @@ object Completion: * For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map * and they never conflict with each other. */ - class Completer(val mode: Mode, val prefix: String, pos: SourcePosition): + class Completer(val mode: Mode, val prefix: String, pos: SourcePosition, prefixFilter: Name => Boolean): /** Completions for terms and types that are currently in scope: * the members of the current class, local definitions and the symbols that have been imported, * recursively adding completions from outer scopes. @@ -524,10 +526,10 @@ object Completion: // There are four possible ways for an extension method to be applicable // 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference. - val termCompleter = new Completer(Mode.Term, prefix, pos) - val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap: - case (name, denots) => denots.collect: - case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName) + val termCompleter = new Completer(Mode.Term, prefix, pos, _.startsWith(prefix)) + val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap { + case (name, denots) => denots.collect { case d: SymDenotation if d.isTerm => (d.termRef, name.asTermName) } + } // 2. The extension method is a member of some given instance that is visible at the point of the reference. val givensInScope = ctx.implicits.eligible(defn.AnyType).map(_.implicitRef.underlyingRef) @@ -556,7 +558,9 @@ object Completion: * 2. satisfy [[Completion.isValidCompletionSymbol]] */ private def include(denot: SingleDenotation, nameInScope: Name)(using Context): Boolean = - nameInScope.startsWith(prefix) && + val sym = denot.symbol + + prefixFilter(nameInScope) && completionsFilter(NoType, nameInScope) && isValidCompletionSymbol(denot.symbol, mode) 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 7a10c9e4804d..fd5ff264e23c 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -102,9 +102,12 @@ class Completions( end if end includeSymbol + val fuzzyMatcher: Name => Boolean = name => + Fuzzy.matchesSubCharacters(completionPos.query, name.toString) + def enrichedCompilerCompletions(qualType: Type): (List[CompletionValue], SymbolSearch.Result) = val compilerCompletions = Completion - .rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath) + .rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath, Some(fuzzyMatcher)) compilerCompletions .toList @@ -116,9 +119,8 @@ class Completions( val (all, result) = if exclusive then (advanced, SymbolSearch.Result.COMPLETE) else - val keywords = KeywordsCompletions.contribute(path, completionPos, comments) + val keywords = KeywordsCompletions.contribute(adjustedPath, completionPos, comments) val allAdvanced = advanced ++ keywords - path match // should not show completions for toplevel case Nil | (_: PackageDef) :: _ if !completionPos.originalCursorPosition.source.file.ext.isScalaScript => diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala index abbf43174bc7..1259a3552228 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala @@ -5,7 +5,7 @@ import scala.meta.internal.pc.Keyword import dotty.tools.dotc.ast.NavigateAST import dotty.tools.dotc.ast.Positioned -import dotty.tools.dotc.ast.tpd.* +import dotty.tools.dotc.ast.untpd.* import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.ast.untpd.UntypedTreeTraverser import dotty.tools.dotc.core.Comments 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 cc6751454d4f..0802bb49963e 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala @@ -711,8 +711,8 @@ class CompletionKeywordSuite extends BaseCompletionSuite: |trait Bar {} |class Baz(b: Int) {} | - |class Foo(x: Int) extends Bar with Baz(1) der@@ - """.stripMargin, + |class Foo(x: Int) extends Bar with Baz(1) @@ + """.stripMargin, """|derives |""".stripMargin ) @@ -730,3 +730,63 @@ class CompletionKeywordSuite extends BaseCompletionSuite: """.stripMargin, "" ) + + @Test def `only-keywords` = + check( + """ + |package foo + | + |object Main { + | class Baz(x: Int) @@ + |} + """.stripMargin, + "" + ) + + @Test def `only-keywords-1` = + check( + """ + |package foo + | + |object Main { + | class Baz(x: Int) + | p@@ + |} + """.stripMargin, + "" + ) + + @Test def `only-keywords-2` = + check( + """ + |package foo + | + |class Baz(x: Int) @@ + """.stripMargin, + "" + ) + + @Test def `def-after-extension` = + check( + """ + |object Main { + | extension (x: Int) @@ + |} + """.stripMargin, + """|derives + |private + |""".stripMargin + ) + + @Test def `def-after-extension-newline` = + check( + """ + |object Main { + | extension (x: Int) + | @@ + |} + """.stripMargin, + """|derives + |private + |""".stripMargin + ) From c2849d4afce1760a439cb0f5f25c3f77f07a5210 Mon Sep 17 00:00:00 2001 From: rochala Date: Fri, 1 Mar 2024 20:19:43 +0100 Subject: [PATCH 2/6] Adjust tests for fuzzy searched completions --- .../tools/pc/completions/Completions.scala | 14 +-- .../pc/completions/KeywordsCompletions.scala | 2 +- .../tools/pc/base/BaseCompletionSuite.scala | 2 +- .../tests/completion/CompletionDocSuite.scala | 4 +- .../completion/CompletionExtensionSuite.scala | 34 +++++-- .../completion/CompletionKeywordSuite.scala | 90 +++---------------- .../completion/CompletionOverrideSuite.scala | 3 +- .../CompletionSnippetNegSuite.scala | 12 +-- .../completion/CompletionSnippetSuite.scala | 7 +- .../pc/tests/completion/CompletionSuite.scala | 50 ++++++++++- 10 files changed, 112 insertions(+), 106 deletions(-) diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala index fd5ff264e23c..226d5dd8fba6 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -16,6 +16,7 @@ import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.core.Comments.Comment import dotty.tools.dotc.core.Constants.Constant import dotty.tools.dotc.core.Contexts.* +import dotty.tools.dotc.core.Denotations.SingleDenotation import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.NameOps.* @@ -26,14 +27,13 @@ import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.core.Types.* import dotty.tools.dotc.interactive.Completion import dotty.tools.dotc.interactive.Completion.Mode +import dotty.tools.dotc.interactive.Interactive import dotty.tools.dotc.util.SourcePosition import dotty.tools.dotc.util.SrcPos import dotty.tools.pc.AutoImports.AutoImportsGenerator -import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor import dotty.tools.pc.buildinfo.BuildInfo +import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor import dotty.tools.pc.utils.MtagsEnrichments.* -import dotty.tools.dotc.core.Denotations.SingleDenotation - class Completions( text: String, @@ -103,7 +103,8 @@ class Completions( end includeSymbol val fuzzyMatcher: Name => Boolean = name => - Fuzzy.matchesSubCharacters(completionPos.query, name.toString) + if completionMode.is(Mode.Member) then CompletionFuzzy.matchesSubCharacters(completionPos.query, name.toString) + else CompletionFuzzy.matches(completionPos.query, name.toString) def enrichedCompilerCompletions(qualType: Type): (List[CompletionValue], SymbolSearch.Result) = val compilerCompletions = Completion @@ -119,8 +120,9 @@ class Completions( val (all, result) = if exclusive then (advanced, SymbolSearch.Result.COMPLETE) else - val keywords = KeywordsCompletions.contribute(adjustedPath, 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 => @@ -423,7 +425,7 @@ class Completions( // class Fo@@ case (td: TypeDef) :: _ - if Fuzzy.matches( + if CompletionFuzzy.matches( td.symbol.name.decoded.replace(Cursor.value, "").nn, filename ) => diff --git a/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala b/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala index 1259a3552228..abbf43174bc7 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala @@ -5,7 +5,7 @@ import scala.meta.internal.pc.Keyword import dotty.tools.dotc.ast.NavigateAST import dotty.tools.dotc.ast.Positioned -import dotty.tools.dotc.ast.untpd.* +import dotty.tools.dotc.ast.tpd.* import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.ast.untpd.UntypedTreeTraverser import dotty.tools.dotc.core.Comments diff --git a/presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala b/presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala index bfb31906bce1..776aab8bc2f7 100644 --- a/presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala @@ -123,7 +123,7 @@ abstract class BaseCompletionSuite extends BasePCSuite: if (assertSingleItem && items.length != 1) then fail( - s"expected single completion item, obtained ${items.length} items.\n${items}" + s"expected single completion item, obtained ${items.length} items.\n${items.map(_.getLabel.nn + "\n")}" ) if (items.size <= itemIndex) then 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 b487611b9ea1..ec0b6dc20688 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionDocSuite.scala @@ -156,8 +156,8 @@ class CompletionDocSuite extends BaseCompletionSuite: |Found documentation for scala/collection/Iterator. |Iterator scala.collection |""".stripMargin, - - includeDocs = true + includeDocs = true, + topLines = Some(1) ) @Test def `scala5` = diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala index f48ba06f699c..09967c41e08d 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala @@ -17,12 +17,14 @@ class CompletionExtensionSuite extends BaseCompletionSuite: |def main = 100.inc@@ |""".stripMargin, """|incr: Int (extension) + |asInstanceOf[X0]: X0 + |isInstanceOf[X0]: Boolean |""".stripMargin ) @Test def `simple-old-syntax` = check( - """|package example + """package example | |object Test: | implicit class TestOps(a: Int): @@ -30,8 +32,9 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | |def main = 100.test@@ |""".stripMargin, - """|testOps(b: Int): String (implicit) - |""".stripMargin + """testOps(b: Int): String (implicit) + |""".stripMargin, + topLines = Some(1) ) @Test def `simple2` = @@ -93,7 +96,10 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | |def main = "foo".iden@@ |""".stripMargin, - """|identity: String (implicit) + """|identity: String (implicit) + |indent(x$0: Int): String + |stripIndent(): String + |isDefinedAt(idx: Int): Boolean |""".stripMargin // identity2 won't be available ) @@ -152,7 +158,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | def incr: Int = num + 1 | |def main = 100.incr - |""".stripMargin + |""".stripMargin, + assertSingleItem = false ) @Test def `simple-edit-old` = @@ -174,7 +181,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | def incr: Int = num + 1 | |def main = 100.incr - |""".stripMargin + |""".stripMargin, + assertSingleItem = false ) @Test def `simple-edit-suffix` = @@ -262,6 +270,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | def main = 100.inc@@ |""".stripMargin, """|incr: Int (extension) + |asInstanceOf[X0]: X0 + |isInstanceOf[X0]: Boolean |""".stripMargin ) @@ -276,6 +286,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | def main = 100.inc@@ |""".stripMargin, """|incr: Int (implicit) + |asInstanceOf[X0]: X0 + |isInstanceOf[X0]: Boolean |""".stripMargin ) @@ -290,6 +302,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | def main = 100.inc@@ |""".stripMargin, """|incr: Int (extension) + |asInstanceOf[X0]: X0 + |isInstanceOf[X0]: Boolean |""".stripMargin ) @@ -304,6 +318,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | def main = 100.inc@@ |""".stripMargin, """|incr: Int (implicit) + |asInstanceOf[X0]: X0 + |isInstanceOf[X0]: Boolean |""".stripMargin ) @@ -391,7 +407,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite: |testVal: Int (implicit) |testVar: Int (implicit) |testOps(b: Int): String (implicit) - |""".stripMargin + |""".stripMargin, + topLines = Some(4) ) @Test def `implicit-val-edit` = @@ -413,5 +430,6 @@ class CompletionExtensionSuite extends BaseCompletionSuite: | val testVal: Int = 42 | |def main = 100.testVal - |""".stripMargin + |""".stripMargin, + assertSingleItem = false ) 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 0802bb49963e..bf7077d47b3f 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala @@ -691,28 +691,26 @@ class CompletionKeywordSuite extends BaseCompletionSuite: @Test def `derives-with-extends` = check( - """ - |package foo - | - |trait Bar {} - |trait Baz {} - | - |class Foo(x: Int) extends Bar with Baz der@@ - """.stripMargin, + """|package foo + | + |trait Bar {} + |trait Baz {} + | + |class Foo(x: Int) extends Bar with Baz der@@ + |""".stripMargin, """|derives |""".stripMargin ) @Test def `derives-with-constructor-extends` = check( - """ - |package foo - | - |trait Bar {} - |class Baz(b: Int) {} - | - |class Foo(x: Int) extends Bar with Baz(1) @@ - """.stripMargin, + """|package foo + | + |trait Bar {} + |class Baz(b: Int) {} + | + |class Foo(x: Int) extends Bar with Baz(1) der@@ + |""".stripMargin, """|derives |""".stripMargin ) @@ -730,63 +728,3 @@ class CompletionKeywordSuite extends BaseCompletionSuite: """.stripMargin, "" ) - - @Test def `only-keywords` = - check( - """ - |package foo - | - |object Main { - | class Baz(x: Int) @@ - |} - """.stripMargin, - "" - ) - - @Test def `only-keywords-1` = - check( - """ - |package foo - | - |object Main { - | class Baz(x: Int) - | p@@ - |} - """.stripMargin, - "" - ) - - @Test def `only-keywords-2` = - check( - """ - |package foo - | - |class Baz(x: Int) @@ - """.stripMargin, - "" - ) - - @Test def `def-after-extension` = - check( - """ - |object Main { - | extension (x: Int) @@ - |} - """.stripMargin, - """|derives - |private - |""".stripMargin - ) - - @Test def `def-after-extension-newline` = - check( - """ - |object Main { - | extension (x: Int) - | @@ - |} - """.stripMargin, - """|derives - |private - |""".stripMargin - ) 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 b3abc1474375..44ceb8cba32b 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionOverrideSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionOverrideSuite.scala @@ -925,12 +925,13 @@ class CompletionOverrideSuite extends BaseCompletionSuite: | def@@ |} |""".stripMargin, + """|def hello1: Int |override def equals(x$0: Any): Boolean |override def hashCode(): Int |""".stripMargin, includeDetail = false, - topLines = Some(3) + topLines = Some(5) ) @Test def `path-dependent` = diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetNegSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetNegSuite.scala index ccd989d811b5..8cbbad0e6ef2 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetNegSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetNegSuite.scala @@ -16,12 +16,12 @@ class CompletionSnippetNegSuite extends BaseCompletionSuite: @Test def `member` = checkSnippet( - """ - |object Main { - | List.appl@@ - |} - |""".stripMargin, - "apply" + """|object Main { + | List.appl@@ + |} + |""".stripMargin, + """|apply + |unapplySeq""".stripMargin ) @Test def `scope` = 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 b601a63ff234..5769304919ca 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSnippetSuite.scala @@ -15,6 +15,7 @@ class CompletionSnippetSuite extends BaseCompletionSuite: |} |""".stripMargin, """|apply($0) + |unapplySeq($0) |""".stripMargin ) @@ -429,7 +430,8 @@ class CompletionSnippetSuite extends BaseCompletionSuite: | extension (s: String) | def bar = 0 | val bar = "abc".bar - """.stripMargin + """.stripMargin, + filter = _.contains("bar: Int") ) // https://github.com/scalameta/metals/issues/4004 @@ -446,5 +448,6 @@ class CompletionSnippetSuite extends BaseCompletionSuite: | extension (s: String) | def bar() = 0 | val bar = "abc".bar() - """.stripMargin + """.stripMargin, + filter = _.contains("bar: Int") ) 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 ebca80dc0717..be2417462860 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -233,6 +233,7 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, """|JavaConverters - scala.collection |JavaConversions - scala.concurrent + |AsJavaConverters - scala.collection.convert |AsJavaConsumer - scala.jdk.FunctionWrappers |AsJavaConverters - scala.collection.convert |FromJavaConsumer - scala.jdk.FunctionWrappers @@ -404,6 +405,7 @@ class CompletionSuite extends BaseCompletionSuite: |Function20 scala |Function21 scala |Function22 scala + |PartialFunction scala |""".stripMargin, topLines = Some(25) ) @@ -795,6 +797,10 @@ class CompletionSuite extends BaseCompletionSuite: |} |""".stripMargin, """|intNumber: Int + |toInt: Int + |instance: Int + |asInstanceOf[X0]: X0 + |isInstanceOf[X0]: Boolean |""".stripMargin ) @@ -1105,7 +1111,8 @@ class CompletionSuite extends BaseCompletionSuite: |} |""".stripMargin, """|first: java.util.List[Int] - |""".stripMargin + |""".stripMargin, + topLines = Some(1) ) @Test def `object-at-type-pos` = @@ -1490,6 +1497,7 @@ class CompletionSuite extends BaseCompletionSuite: """|object O: | val a = List.apply($0) |""".stripMargin, + assertSingleItem = false ) @Test def `multiline-comment` = @@ -1557,6 +1565,16 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, """Set scala.collection |SetOps scala.collection + |GenSet scala.collection + |AbstractSet scala.collection + |BitSet scala.collection + |BitSetOps scala.collection + |SortedSet scala.collection + |SortedSetFactoryDefaults scala.collection + |SortedSetOps scala.collection + |StrictOptimizedSetOps scala.collection + |StrictOptimizedSortedSetOps scala.collection + |GenSet = scala.collection.Set[X] |""".stripMargin ) @@ -1566,6 +1584,16 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, """Set scala.collection |SetOps scala.collection + |GenSet scala.collection + |AbstractSet scala.collection + |BitSet scala.collection + |BitSetOps scala.collection + |SortedSet scala.collection + |SortedSetFactoryDefaults scala.collection + |SortedSetOps scala.collection + |StrictOptimizedSetOps scala.collection + |StrictOptimizedSortedSetOps scala.collection + |GenSet = scala.collection.Set[X] |""".stripMargin, ) @@ -1606,7 +1634,8 @@ class CompletionSuite extends BaseCompletionSuite: | List(1,2,3).tes@@ |""".stripMargin, """|test(p: Int => Boolean): List[Int] - |""".stripMargin + |""".stripMargin, + topLines = Some(1) ) @Test def `old-style-extension-type-variable-inference` = @@ -1618,7 +1647,8 @@ class CompletionSuite extends BaseCompletionSuite: | List(1,2,3).tes@@ |""".stripMargin, """|test(p: Int => Boolean): List[Int] - |""".stripMargin + |""".stripMargin, + topLines = Some(1) ) @Test def `instantiate-type-vars-in-extra-apply-completions` = @@ -1774,3 +1804,17 @@ class CompletionSuite extends BaseCompletionSuite: filter = _ == "Override java.lang" ) + @Test def `fuzzy-search-test` = + check( + """| + |object MyInterface { + | def someMethod(x: Int): Int = ??? + |} + |object Test { + | MyInterface.m@@ + |} + |""".stripMargin, + """|someMethod(x: Int): Int + |""".stripMargin, + topLines = Some(1) + ) From 33f332a363ee339d877c53b6d60e45dc23962641 Mon Sep 17 00:00:00 2001 From: rochala Date: Sat, 9 Mar 2024 12:40:04 +0100 Subject: [PATCH 3/6] Small refactor --- .../tools/dotc/interactive/Completion.scala | 3 +-- .../dotty/tools/dotc/typer/Implicits.scala | 2 +- .../tools/pc/completions/Completions.scala | 2 +- .../pc/tests/completion/CompletionSuite.scala | 22 ++++++++++++++++++- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index b1b8b1dcc98a..8f4f8e4da143 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -526,7 +526,7 @@ object Completion: // There are four possible ways for an extension method to be applicable // 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference. - val termCompleter = new Completer(Mode.Term, prefix, pos, _.startsWith(prefix)) + val termCompleter = new Completer(Mode.Term, prefix, pos, prefixFilter) val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap { case (name, denots) => denots.collect { case d: SymDenotation if d.isTerm => (d.termRef, name.asTermName) } } @@ -609,7 +609,6 @@ object Completion: private def implicitConversionTargets(qual: tpd.Tree)(using Context): Set[SearchSuccess] = { val typer = ctx.typer val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span).allImplicits - conversions.map(_.tree.typeOpt) interactiv.println(i"implicit conversion targets considered: ${conversions.toList}%, %") conversions diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 5ffc81744d85..ffd9d7fd8515 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -531,7 +531,7 @@ object Implicits: |must be more specific than $target""" :: Nil override def msg(using Context) = - super.msg.append("\nThe expected type $target is not specific enough, so no search was attempted") + super.msg.append(i"\nThe expected type $target is not specific enough, so no search was attempted") override def toString = s"TooUnspecific" end TooUnspecific 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 226d5dd8fba6..3f2d89a15b72 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala @@ -102,7 +102,7 @@ class Completions( end if end includeSymbol - val fuzzyMatcher: Name => Boolean = name => + lazy val fuzzyMatcher: Name => Boolean = name => if completionMode.is(Mode.Member) then CompletionFuzzy.matchesSubCharacters(completionPos.query, name.toString) else CompletionFuzzy.matches(completionPos.query, name.toString) 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 be2417462860..8736584228a3 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -533,7 +533,6 @@ class CompletionSuite extends BaseCompletionSuite: |until(end: Long): Exclusive[Long] |until(end: Long, step: Long): Exclusive[Long] |""".stripMargin, - postProcessObtained = _.replace("Float", "Double"), stableOrder = false ) @@ -1818,3 +1817,24 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, topLines = Some(1) ) + + @Test def `fuzzy-search-test-multiple` = + check( + """| + |trait MyInterface { + | def someMethod(x: Int): Int = ??? + |} + |object Test { + | extension (interface: MyInterface) def someExtMethod(x: Int): Int = ??? + | implicit class MyInterfaceExtension(interface: MyInterface): + | def someOldExtMethod(x: Int): Int = ??? + | val x: MyInterface = ??? + | x.m@@ + |} + |""".stripMargin, + """|someMethod(x: Int): Int + |someExtMethod(x: Int): Int + |someOldExtMethod(x: Int): Int + |""".stripMargin, + topLines = Some(3) + ) From 2de3b85f8b5835c253065680489a1eda0928598c Mon Sep 17 00:00:00 2001 From: rochala Date: Fri, 12 Apr 2024 13:32:15 +0200 Subject: [PATCH 4/6] Fix tests after deduplication fix --- .../completion/CompletionOverrideSuite.scala | 2 ++ .../pc/tests/completion/CompletionSuite.scala | 15 ++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) 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 44ceb8cba32b..94c444b0feb9 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionOverrideSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionOverrideSuite.scala @@ -929,6 +929,8 @@ class CompletionOverrideSuite extends BaseCompletionSuite: """|def hello1: Int |override def equals(x$0: Any): Boolean |override def hashCode(): Int + |override def toString(): String + |override val hello2: Int |""".stripMargin, includeDetail = false, topLines = Some(5) 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 8736584228a3..138adcb35e47 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -233,7 +233,6 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, """|JavaConverters - scala.collection |JavaConversions - scala.concurrent - |AsJavaConverters - scala.collection.convert |AsJavaConsumer - scala.jdk.FunctionWrappers |AsJavaConverters - scala.collection.convert |FromJavaConsumer - scala.jdk.FunctionWrappers @@ -1342,7 +1341,15 @@ class CompletionSuite extends BaseCompletionSuite: |FormView - javax.swing.text.html |Formatter - java.util |Formatter - java.util.logging - |FocusEvent - java.awt.event""".stripMargin + |FocusEvent - java.awt.event + |ClassFormatError java.lang + |ClassNotFoundException java.lang + |NumberFormatException java.lang + |NoClassDefFoundError java.lang + |NumberFormatException java.lang + |StringFormat scala.Predef + |StringFormat[A] scala.Predef + """.stripMargin @Test def `extension-definition-scope` = check( @@ -1557,14 +1564,12 @@ class CompletionSuite extends BaseCompletionSuite: assertSingleItem = false ) - @Test def `multi-export` = check( """export scala.collection.{AbstractMap, Set@@} |""".stripMargin, """Set scala.collection |SetOps scala.collection - |GenSet scala.collection |AbstractSet scala.collection |BitSet scala.collection |BitSetOps scala.collection @@ -1583,7 +1588,6 @@ class CompletionSuite extends BaseCompletionSuite: |""".stripMargin, """Set scala.collection |SetOps scala.collection - |GenSet scala.collection |AbstractSet scala.collection |BitSet scala.collection |BitSetOps scala.collection @@ -1672,6 +1676,7 @@ class CompletionSuite extends BaseCompletionSuite: |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 + |LazyList[A](elems: A*): LazyList[A] |""".stripMargin, filter = _.contains("[") ) From 0f080deeb560f06f28307d1dd939a4a94935151f Mon Sep 17 00:00:00 2001 From: rochala Date: Fri, 12 Apr 2024 15:07:47 +0200 Subject: [PATCH 5/6] Apply review comments, limit completion results to avoid flakiness --- .../tools/dotc/interactive/Completion.scala | 27 +++++------ .../pc/tests/completion/CompletionSuite.scala | 45 +++++++++---------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/interactive/Completion.scala b/compiler/src/dotty/tools/dotc/interactive/Completion.scala index 8f4f8e4da143..2ff8ad1c6535 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Completion.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Completion.scala @@ -72,10 +72,10 @@ object Completion: rawPrefix: String, tpdPath: List[tpd.Tree], untpdPath: List[untpd.Tree], - prefixFilter: Option[Name => Boolean] = None + customMatcher: Option[Name => Boolean] = None )(using Context): CompletionMap = val adjustedPath = typeCheckExtensionConstructPath(untpdPath, tpdPath, pos) - computeCompletions(pos, mode, rawPrefix, adjustedPath, prefixFilter) + computeCompletions(pos, mode, rawPrefix, adjustedPath, customMatcher) /** * Inspect `path` to determine what kinds of symbols should be considered. @@ -194,12 +194,12 @@ object Completion: .flatten.getOrElse(tpdPath) private def computeCompletions( - pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree], prefixFilter: Option[Name => Boolean] + pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree], matches: Option[Name => Boolean] )(using Context): CompletionMap = val hasBackTick = rawPrefix.headOption.contains('`') val prefix = if hasBackTick then rawPrefix.drop(1) else rawPrefix - val prefixFilter0 = prefixFilter.getOrElse(_.startsWith(prefix)) - val completer = new Completer(mode, prefix, pos, prefixFilter0) + val matches0 = matches.getOrElse(_.startsWith(prefix)) + val completer = new Completer(mode, pos, matches0) val result = adjustedPath match // Ignore synthetic select from `This` because in code it was `Ident` @@ -211,7 +211,6 @@ object Completion: case _ => completer.scopeCompletions interactiv.println(i"""completion info with pos = $pos, - | prefix = ${completer.prefix}, | term = ${completer.mode.is(Mode.Term)}, | type = ${completer.mode.is(Mode.Type)}, | scope = ${completer.mode.is(Mode.Scope)}, @@ -313,13 +312,13 @@ object Completion: /** Computes code completions depending on the context in which completion is requested * @param mode Should complete names of terms, types or both - * @param prefix The prefix that all suggested completions should start with * @param pos Cursor position where completion was requested + * @param matches Function taking name used to filter completions * * For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map * and they never conflict with each other. */ - class Completer(val mode: Mode, val prefix: String, pos: SourcePosition, prefixFilter: Name => Boolean): + class Completer(val mode: Mode, pos: SourcePosition, matches: Name => Boolean): /** Completions for terms and types that are currently in scope: * the members of the current class, local definitions and the symbols that have been imported, * recursively adding completions from outer scopes. @@ -526,10 +525,10 @@ object Completion: // There are four possible ways for an extension method to be applicable // 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference. - val termCompleter = new Completer(Mode.Term, prefix, pos, prefixFilter) - val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap { - case (name, denots) => denots.collect { case d: SymDenotation if d.isTerm => (d.termRef, name.asTermName) } - } + val termCompleter = new Completer(Mode.Term, pos, matches) + val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap: + case (name, denots) => denots.collect: + case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName) // 2. The extension method is a member of some given instance that is visible at the point of the reference. val givensInScope = ctx.implicits.eligible(defn.AnyType).map(_.implicitRef.underlyingRef) @@ -558,9 +557,7 @@ object Completion: * 2. satisfy [[Completion.isValidCompletionSymbol]] */ private def include(denot: SingleDenotation, nameInScope: Name)(using Context): Boolean = - val sym = denot.symbol - - prefixFilter(nameInScope) && + matches(nameInScope) && completionsFilter(NoType, nameInScope) && isValidCompletionSymbol(denot.symbol, mode) 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 138adcb35e47..10a57f705ceb 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionSuite.scala @@ -1334,21 +1334,6 @@ class CompletionSuite extends BaseCompletionSuite: 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 - |ClassFormatError java.lang - |ClassNotFoundException java.lang - |NumberFormatException java.lang - |NoClassDefFoundError java.lang - |NumberFormatException java.lang - |StringFormat scala.Predef - |StringFormat[A] scala.Predef """.stripMargin @Test def `extension-definition-scope` = @@ -1357,7 +1342,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (x: Fo@@) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-symbol-search` = @@ -1376,7 +1362,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A <: Fo@@] |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-type-parameter-symbol-search` = @@ -1395,7 +1382,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (using Fo@@) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @@ -1405,7 +1393,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (x: Int)(using Fo@@) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-mix-2` = @@ -1414,7 +1403,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (using Fo@@)(x: Int)(using Foo) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-mix-3` = @@ -1423,7 +1413,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension (using Foo)(x: Int)(using Fo@@) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-mix-4` = @@ -1432,7 +1423,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A](x: Fo@@) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-mix-5` = @@ -1441,7 +1433,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A](using Fo@@)(x: Int) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-mix-6` = @@ -1450,7 +1443,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A](using Foo)(x: Fo@@) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-mix-7` = @@ -1459,7 +1453,8 @@ class CompletionSuite extends BaseCompletionSuite: |object T: | extension [A](using Foo)(x: Fo@@)(using Foo) |""".stripMargin, - extensionResult + extensionResult, + topLines = Some(2) ) @Test def `extension-definition-select` = From 8e2ad6f9fd3656e9cc5957707d6bc47e76b53b73 Mon Sep 17 00:00:00 2001 From: rochala Date: Fri, 12 Apr 2024 17:18:06 +0200 Subject: [PATCH 6/6] filter extension tests to avoid failure in java 8 + windows CI --- .../pc/tests/completion/CompletionExtensionSuite.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala index 09967c41e08d..e67c31329c1c 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionExtensionSuite.scala @@ -97,10 +97,9 @@ class CompletionExtensionSuite extends BaseCompletionSuite: |def main = "foo".iden@@ |""".stripMargin, """|identity: String (implicit) - |indent(x$0: Int): String - |stripIndent(): String - |isDefinedAt(idx: Int): Boolean - |""".stripMargin // identity2 won't be available + |""".stripMargin, // identity2 won't be available + filter = _.contains("(implicit)") + ) @Test def `filter-by-type-subtype` =