Skip to content

Commit 6e02dc3

Browse files
committed
Initial fuzzy search
1 parent 6b52789 commit 6e02dc3

File tree

4 files changed

+81
-17
lines changed

4 files changed

+81
-17
lines changed

compiler/src/dotty/tools/dotc/interactive/Completion.scala

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,11 @@ object Completion:
7171
mode: Mode,
7272
rawPrefix: String,
7373
tpdPath: List[tpd.Tree],
74-
untpdPath: List[untpd.Tree]
74+
untpdPath: List[untpd.Tree],
75+
prefixFilter: Option[Name => Boolean] = None
7576
)(using Context): CompletionMap =
7677
val adjustedPath = typeCheckExtensionConstructPath(untpdPath, tpdPath, pos)
77-
computeCompletions(pos, mode, rawPrefix, adjustedPath)
78+
computeCompletions(pos, mode, rawPrefix, adjustedPath, prefixFilter)
7879

7980
/**
8081
* Inspect `path` to determine what kinds of symbols should be considered.
@@ -198,11 +199,12 @@ object Completion:
198199
.flatten.getOrElse(tpdPath)
199200

200201
private def computeCompletions(
201-
pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree]
202+
pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree], prefixFilter: Option[Name => Boolean]
202203
)(using Context): CompletionMap =
203204
val hasBackTick = rawPrefix.headOption.contains('`')
204205
val prefix = if hasBackTick then rawPrefix.drop(1) else rawPrefix
205-
val completer = new Completer(mode, prefix, pos)
206+
val prefixFilter0 = prefixFilter.getOrElse(_.startsWith(prefix))
207+
val completer = new Completer(mode, prefix, pos, prefixFilter0)
206208

207209
val result = adjustedPath match
208210
// Ignore synthetic select from `This` because in code it was `Ident`
@@ -296,7 +298,7 @@ object Completion:
296298
* For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map
297299
* and they never conflict with each other.
298300
*/
299-
class Completer(val mode: Mode, val prefix: String, pos: SourcePosition):
301+
class Completer(val mode: Mode, val prefix: String, pos: SourcePosition, prefixFilter: Name => Boolean):
300302
/** Completions for terms and types that are currently in scope:
301303
* the members of the current class, local definitions and the symbols that have been imported,
302304
* recursively adding completions from outer scopes.
@@ -503,10 +505,10 @@ object Completion:
503505
// There are four possible ways for an extension method to be applicable
504506

505507
// 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference.
506-
val termCompleter = new Completer(Mode.Term, prefix, pos)
507-
val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap:
508-
case (name, denots) => denots.collect:
509-
case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName)
508+
val termCompleter = new Completer(Mode.Term, prefix, pos, _.startsWith(prefix))
509+
val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap {
510+
case (name, denots) => denots.collect { case d: SymDenotation if d.isTerm => (d.termRef, name.asTermName) }
511+
}
510512

511513
// 2. The extension method is a member of some given instance that is visible at the point of the reference.
512514
val givensInScope = ctx.implicits.eligible(defn.AnyType).map(_.implicitRef.underlyingRef)
@@ -545,7 +547,7 @@ object Completion:
545547
val sym = denot.symbol
546548

547549

548-
nameInScope.startsWith(prefix) &&
550+
prefixFilter(nameInScope) &&
549551
sym.exists &&
550552
completionsFilter(NoType, nameInScope) &&
551553
!sym.isAbsent() &&

presentation-compiler/src/main/dotty/tools/pc/completions/Completions.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,24 +110,26 @@ class Completions(
110110
val (all, result) =
111111
if exclusive then (advanced, SymbolSearch.Result.COMPLETE)
112112
else
113-
val keywords =
114-
KeywordsCompletions.contribute(path, completionPos, comments)
113+
val keywords = KeywordsCompletions.contribute(adjustedPath, completionPos, comments)
115114
val allAdvanced = advanced ++ keywords
115+
val fuzzyMatcher: Name => Boolean = name =>
116+
Fuzzy.matchesSubCharacters(completionPos.query, name.toString)
117+
116118
path match
117119
// should not show completions for toplevel
118120
case Nil | (_: PackageDef) :: _ if completionPos.originalCursorPosition.source.file.extension != "sc" =>
119121
(allAdvanced, SymbolSearch.Result.COMPLETE)
120122
case Select(qual, _) :: _ if qual.typeOpt.isErroneous =>
121123
(allAdvanced, SymbolSearch.Result.COMPLETE)
122124
case Select(qual, _) :: _ =>
123-
val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath)
125+
val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath, Some(fuzzyMatcher))
124126
val (compiler, result) = compilerCompletions
125127
.toList
126128
.flatMap(toCompletionValues)
127129
.filterInteresting(qual.typeOpt.widenDealias)
128130
(allAdvanced ++ compiler, result)
129131
case _ =>
130-
val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath)
132+
val compilerCompletions = Completion.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath, Some(fuzzyMatcher))
131133
val (compiler, result) = compilerCompletions
132134
.toList
133135
.flatMap(toCompletionValues)

presentation-compiler/src/main/dotty/tools/pc/completions/KeywordsCompletions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import scala.meta.internal.pc.Keyword
55

66
import dotty.tools.dotc.ast.NavigateAST
77
import dotty.tools.dotc.ast.Positioned
8-
import dotty.tools.dotc.ast.tpd.*
8+
import dotty.tools.dotc.ast.untpd.*
99
import dotty.tools.dotc.ast.untpd
1010
import dotty.tools.dotc.ast.untpd.UntypedTreeTraverser
1111
import dotty.tools.dotc.core.Comments

presentation-compiler/test/dotty/tools/pc/tests/completion/CompletionKeywordSuite.scala

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -718,8 +718,8 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
718718
|trait Bar {}
719719
|class Baz(b: Int) {}
720720
|
721-
|class Foo(x: Int) extends Bar with Baz(1) der@@
722-
""".stripMargin,
721+
|class Foo(x: Int) extends Bar with Baz(1) @@
722+
""".stripMargin,
723723
"""|derives
724724
|""".stripMargin
725725
)
@@ -737,3 +737,63 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
737737
""".stripMargin,
738738
""
739739
)
740+
741+
@Test def `only-keywords` =
742+
check(
743+
"""
744+
|package foo
745+
|
746+
|object Main {
747+
| class Baz(x: Int) @@
748+
|}
749+
""".stripMargin,
750+
""
751+
)
752+
753+
@Test def `only-keywords-1` =
754+
check(
755+
"""
756+
|package foo
757+
|
758+
|object Main {
759+
| class Baz(x: Int)
760+
| p@@
761+
|}
762+
""".stripMargin,
763+
""
764+
)
765+
766+
@Test def `only-keywords-2` =
767+
check(
768+
"""
769+
|package foo
770+
|
771+
|class Baz(x: Int) @@
772+
""".stripMargin,
773+
""
774+
)
775+
776+
@Test def `def-after-extension` =
777+
check(
778+
"""
779+
|object Main {
780+
| extension (x: Int) @@
781+
|}
782+
""".stripMargin,
783+
"""|derives
784+
|private
785+
|""".stripMargin
786+
)
787+
788+
@Test def `def-after-extension-newline` =
789+
check(
790+
"""
791+
|object Main {
792+
| extension (x: Int)
793+
| @@
794+
|}
795+
""".stripMargin,
796+
"""|derives
797+
|private
798+
|""".stripMargin
799+
)

0 commit comments

Comments
 (0)