Skip to content

Commit d8efa12

Browse files
Backport "Add custom matchers for completions (fuzzy search for presentation compiler)" to LTS (#21062)
Backports #19850 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents d9cdfe5 + 4fdde6b commit d8efa12

File tree

11 files changed

+166
-76
lines changed

11 files changed

+166
-76
lines changed

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

+10-10
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+
customMatcher: 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, customMatcher)
7879

7980
/**
8081
* Inspect `path` to determine what kinds of symbols should be considered.
@@ -193,11 +194,12 @@ object Completion:
193194
.flatten.getOrElse(tpdPath)
194195

195196
private def computeCompletions(
196-
pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree]
197+
pos: SourcePosition, mode: Mode, rawPrefix: String, adjustedPath: List[tpd.Tree], matches: Option[Name => Boolean]
197198
)(using Context): CompletionMap =
198199
val hasBackTick = rawPrefix.headOption.contains('`')
199200
val prefix = if hasBackTick then rawPrefix.drop(1) else rawPrefix
200-
val completer = new Completer(mode, prefix, pos)
201+
val matches0 = matches.getOrElse(_.startsWith(prefix))
202+
val completer = new Completer(mode, pos, matches0)
201203

202204
val result = adjustedPath match
203205
// Ignore synthetic select from `This` because in code it was `Ident`
@@ -209,7 +211,6 @@ object Completion:
209211
case _ => completer.scopeCompletions
210212

211213
interactiv.println(i"""completion info with pos = $pos,
212-
| prefix = ${completer.prefix},
213214
| term = ${completer.mode.is(Mode.Term)},
214215
| type = ${completer.mode.is(Mode.Type)},
215216
| scope = ${completer.mode.is(Mode.Scope)},
@@ -311,13 +312,13 @@ object Completion:
311312

312313
/** Computes code completions depending on the context in which completion is requested
313314
* @param mode Should complete names of terms, types or both
314-
* @param prefix The prefix that all suggested completions should start with
315315
* @param pos Cursor position where completion was requested
316+
* @param matches Function taking name used to filter completions
316317
*
317318
* For the results of all `xyzCompletions` methods term names and type names are always treated as different keys in the same map
318319
* and they never conflict with each other.
319320
*/
320-
class Completer(val mode: Mode, val prefix: String, pos: SourcePosition):
321+
class Completer(val mode: Mode, pos: SourcePosition, matches: Name => Boolean):
321322
/** Completions for terms and types that are currently in scope:
322323
* the members of the current class, local definitions and the symbols that have been imported,
323324
* recursively adding completions from outer scopes.
@@ -524,7 +525,7 @@ object Completion:
524525
// There are four possible ways for an extension method to be applicable
525526

526527
// 1. The extension method is visible under a simple name, by being defined or inherited or imported in a scope enclosing the reference.
527-
val termCompleter = new Completer(Mode.Term, prefix, pos)
528+
val termCompleter = new Completer(Mode.Term, pos, matches)
528529
val extMethodsInScope = termCompleter.scopeCompletions.toList.flatMap:
529530
case (name, denots) => denots.collect:
530531
case d: SymDenotation if d.isTerm && d.termRef.symbol.is(Extension) => (d.termRef, name.asTermName)
@@ -556,7 +557,7 @@ object Completion:
556557
* 2. satisfy [[Completion.isValidCompletionSymbol]]
557558
*/
558559
private def include(denot: SingleDenotation, nameInScope: Name)(using Context): Boolean =
559-
nameInScope.startsWith(prefix) &&
560+
matches(nameInScope) &&
560561
completionsFilter(NoType, nameInScope) &&
561562
isValidCompletionSymbol(denot.symbol, mode)
562563

@@ -605,7 +606,6 @@ object Completion:
605606
private def implicitConversionTargets(qual: tpd.Tree)(using Context): Set[SearchSuccess] = {
606607
val typer = ctx.typer
607608
val conversions = new typer.ImplicitSearch(defn.AnyType, qual, pos.span).allImplicits
608-
conversions.map(_.tree.typeOpt)
609609

610610
interactiv.println(i"implicit conversion targets considered: ${conversions.toList}%, %")
611611
conversions

compiler/src/dotty/tools/dotc/typer/Implicits.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,8 @@ object Implicits:
509509
|must be more specific than $target"""
510510

511511
override def msg(using Context) =
512-
super.msg.append("\nThe expected type $target is not specific enough, so no search was attempted")
512+
super.msg.append(i"\nThe expected type $target is not specific enough, so no search was attempted")
513+
513514
override def toString = s"TooUnspecific"
514515

515516
/** An ambiguous implicits failure */

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

+9-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import dotty.tools.dotc.ast.untpd
1616
import dotty.tools.dotc.core.Comments.Comment
1717
import dotty.tools.dotc.core.Constants.Constant
1818
import dotty.tools.dotc.core.Contexts.*
19+
import dotty.tools.dotc.core.Denotations.SingleDenotation
1920
import dotty.tools.dotc.core.Flags
2021
import dotty.tools.dotc.core.Flags.*
2122
import dotty.tools.dotc.core.NameOps.*
@@ -26,14 +27,13 @@ import dotty.tools.dotc.core.Symbols.*
2627
import dotty.tools.dotc.core.Types.*
2728
import dotty.tools.dotc.interactive.Completion
2829
import dotty.tools.dotc.interactive.Completion.Mode
30+
import dotty.tools.dotc.interactive.Interactive
2931
import dotty.tools.dotc.util.SourcePosition
3032
import dotty.tools.dotc.util.SrcPos
3133
import dotty.tools.pc.AutoImports.AutoImportsGenerator
32-
import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor
3334
import dotty.tools.pc.buildinfo.BuildInfo
35+
import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor
3436
import dotty.tools.pc.utils.MtagsEnrichments.*
35-
import dotty.tools.dotc.core.Denotations.SingleDenotation
36-
3737

3838
class Completions(
3939
text: String,
@@ -102,9 +102,13 @@ class Completions(
102102
end if
103103
end includeSymbol
104104

105+
lazy val fuzzyMatcher: Name => Boolean = name =>
106+
if completionMode.is(Mode.Member) then CompletionFuzzy.matchesSubCharacters(completionPos.query, name.toString)
107+
else CompletionFuzzy.matches(completionPos.query, name.toString)
108+
105109
def enrichedCompilerCompletions(qualType: Type): (List[CompletionValue], SymbolSearch.Result) =
106110
val compilerCompletions = Completion
107-
.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath)
111+
.rawCompletions(completionPos.originalCursorPosition, completionMode, completionPos.query, path, adjustedPath, Some(fuzzyMatcher))
108112

109113
compilerCompletions
110114
.toList
@@ -423,7 +427,7 @@ class Completions(
423427

424428
// class Fo@@
425429
case (td: TypeDef) :: _
426-
if Fuzzy.matches(
430+
if CompletionFuzzy.matches(
427431
td.symbol.name.decoded.replace(Cursor.value, "").nn,
428432
filename
429433
) =>

presentation-compiler/test/dotty/tools/pc/base/BaseCompletionSuite.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ abstract class BaseCompletionSuite extends BasePCSuite:
123123

124124
if (assertSingleItem && items.length != 1) then
125125
fail(
126-
s"expected single completion item, obtained ${items.length} items.\n${items}"
126+
s"expected single completion item, obtained ${items.length} items.\n${items.map(_.getLabel.nn + "\n")}"
127127
)
128128

129129
if (items.size <= itemIndex) then

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ class CompletionDocSuite extends BaseCompletionSuite:
156156
|Found documentation for scala/collection/Iterator.
157157
|Iterator scala.collection
158158
|""".stripMargin,
159-
160-
includeDocs = true
159+
includeDocs = true,
160+
topLines = Some(1)
161161
)
162162

163163
@Test def `scala5` =

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

+26-9
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,24 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
1717
|def main = 100.inc@@
1818
|""".stripMargin,
1919
"""|incr: Int (extension)
20+
|asInstanceOf[X0]: X0
21+
|isInstanceOf[X0]: Boolean
2022
|""".stripMargin
2123
)
2224

2325
@Test def `simple-old-syntax` =
2426
check(
25-
"""|package example
27+
"""package example
2628
|
2729
|object Test:
2830
| implicit class TestOps(a: Int):
2931
| def testOps(b: Int): String = ???
3032
|
3133
|def main = 100.test@@
3234
|""".stripMargin,
33-
"""|testOps(b: Int): String (implicit)
34-
|""".stripMargin
35+
"""testOps(b: Int): String (implicit)
36+
|""".stripMargin,
37+
topLines = Some(1)
3538
)
3639

3740
@Test def `simple2` =
@@ -93,8 +96,10 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
9396
|
9497
|def main = "foo".iden@@
9598
|""".stripMargin,
96-
"""|identity: String (implicit)
97-
|""".stripMargin // identity2 won't be available
99+
"""|identity: String (implicit)
100+
|""".stripMargin, // identity2 won't be available
101+
filter = _.contains("(implicit)")
102+
98103
)
99104

100105
@Test def `filter-by-type-subtype` =
@@ -152,7 +157,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
152157
| def incr: Int = num + 1
153158
|
154159
|def main = 100.incr
155-
|""".stripMargin
160+
|""".stripMargin,
161+
assertSingleItem = false
156162
)
157163

158164
@Test def `simple-edit-old` =
@@ -174,7 +180,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
174180
| def incr: Int = num + 1
175181
|
176182
|def main = 100.incr
177-
|""".stripMargin
183+
|""".stripMargin,
184+
assertSingleItem = false
178185
)
179186

180187
@Test def `simple-edit-suffix` =
@@ -262,6 +269,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
262269
| def main = 100.inc@@
263270
|""".stripMargin,
264271
"""|incr: Int (extension)
272+
|asInstanceOf[X0]: X0
273+
|isInstanceOf[X0]: Boolean
265274
|""".stripMargin
266275
)
267276

@@ -276,6 +285,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
276285
| def main = 100.inc@@
277286
|""".stripMargin,
278287
"""|incr: Int (implicit)
288+
|asInstanceOf[X0]: X0
289+
|isInstanceOf[X0]: Boolean
279290
|""".stripMargin
280291
)
281292

@@ -290,6 +301,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
290301
| def main = 100.inc@@
291302
|""".stripMargin,
292303
"""|incr: Int (extension)
304+
|asInstanceOf[X0]: X0
305+
|isInstanceOf[X0]: Boolean
293306
|""".stripMargin
294307
)
295308

@@ -304,6 +317,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
304317
| def main = 100.inc@@
305318
|""".stripMargin,
306319
"""|incr: Int (implicit)
320+
|asInstanceOf[X0]: X0
321+
|isInstanceOf[X0]: Boolean
307322
|""".stripMargin
308323
)
309324

@@ -391,7 +406,8 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
391406
|testVal: Int (implicit)
392407
|testVar: Int (implicit)
393408
|testOps(b: Int): String (implicit)
394-
|""".stripMargin
409+
|""".stripMargin,
410+
topLines = Some(4)
395411
)
396412

397413
@Test def `implicit-val-edit` =
@@ -413,5 +429,6 @@ class CompletionExtensionSuite extends BaseCompletionSuite:
413429
| val testVal: Int = 42
414430
|
415431
|def main = 100.testVal
416-
|""".stripMargin
432+
|""".stripMargin,
433+
assertSingleItem = false
417434
)

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

+14-16
Original file line numberDiff line numberDiff line change
@@ -695,28 +695,26 @@ class CompletionKeywordSuite extends BaseCompletionSuite:
695695

696696
@Test def `derives-with-extends` =
697697
check(
698-
"""
699-
|package foo
700-
|
701-
|trait Bar {}
702-
|trait Baz {}
703-
|
704-
|class Foo(x: Int) extends Bar with Baz der@@
705-
""".stripMargin,
698+
"""|package foo
699+
|
700+
|trait Bar {}
701+
|trait Baz {}
702+
|
703+
|class Foo(x: Int) extends Bar with Baz der@@
704+
|""".stripMargin,
706705
"""|derives
707706
|""".stripMargin
708707
)
709708

710709
@Test def `derives-with-constructor-extends` =
711710
check(
712-
"""
713-
|package foo
714-
|
715-
|trait Bar {}
716-
|class Baz(b: Int) {}
717-
|
718-
|class Foo(x: Int) extends Bar with Baz(1) der@@
719-
""".stripMargin,
711+
"""|package foo
712+
|
713+
|trait Bar {}
714+
|class Baz(b: Int) {}
715+
|
716+
|class Foo(x: Int) extends Bar with Baz(1) der@@
717+
|""".stripMargin,
720718
"""|derives
721719
|""".stripMargin
722720
)

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -925,12 +925,15 @@ class CompletionOverrideSuite extends BaseCompletionSuite:
925925
| def@@
926926
|}
927927
|""".stripMargin,
928+
928929
"""|def hello1: Int
929930
|override def equals(x$0: Any): Boolean
930931
|override def hashCode(): Int
932+
|override def toString(): String
933+
|override val hello2: Int
931934
|""".stripMargin,
932935
includeDetail = false,
933-
topLines = Some(3)
936+
topLines = Some(5)
934937
)
935938

936939
@Test def `path-dependent` =

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ class CompletionSnippetNegSuite extends BaseCompletionSuite:
1616

1717
@Test def `member` =
1818
checkSnippet(
19-
"""
20-
|object Main {
21-
| List.appl@@
22-
|}
23-
|""".stripMargin,
24-
"apply"
19+
"""|object Main {
20+
| List.appl@@
21+
|}
22+
|""".stripMargin,
23+
"""|apply
24+
|unapplySeq""".stripMargin
2525
)
2626

2727
@Test def `scope` =

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class CompletionSnippetSuite extends BaseCompletionSuite:
1515
|}
1616
|""".stripMargin,
1717
"""|apply($0)
18+
|unapplySeq($0)
1819
|""".stripMargin
1920
)
2021

@@ -429,7 +430,8 @@ class CompletionSnippetSuite extends BaseCompletionSuite:
429430
| extension (s: String)
430431
| def bar = 0
431432
| val bar = "abc".bar
432-
""".stripMargin
433+
""".stripMargin,
434+
filter = _.contains("bar: Int")
433435
)
434436

435437
// https://github.com/scalameta/metals/issues/4004
@@ -446,5 +448,6 @@ class CompletionSnippetSuite extends BaseCompletionSuite:
446448
| extension (s: String)
447449
| def bar() = 0
448450
| val bar = "abc".bar()
449-
""".stripMargin
451+
""".stripMargin,
452+
filter = _.contains("bar: Int")
450453
)

0 commit comments

Comments
 (0)