Skip to content

Commit ae1b409

Browse files
committed
refactor cursorPos to reuse compiler completion mode
1 parent 1dc4c42 commit ae1b409

File tree

4 files changed

+53
-120
lines changed

4 files changed

+53
-120
lines changed

language-server/test/dotty/tools/languageserver/CompletionTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1529,7 +1529,7 @@ class CompletionTest {
15291529
)
15301530
}
15311531

1532-
@Test def desugaredErrorStatement: Unit =
1532+
@Test def extensionDefinitionCompletions: Unit =
15331533
code"""|trait Foo
15341534
|object T:
15351535
| extension (x: Fo$m1)

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

Lines changed: 43 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering}
1212
import scala.meta.pc.*
1313

1414
import dotty.tools.dotc.ast.tpd.*
15+
import dotty.tools.dotc.ast.NavigateAST
1516
import dotty.tools.dotc.core.Comments.Comment
1617
import dotty.tools.dotc.core.Constants.Constant
1718
import dotty.tools.dotc.core.Contexts.*
@@ -24,6 +25,7 @@ import dotty.tools.dotc.core.StdNames.*
2425
import dotty.tools.dotc.core.Symbols.*
2526
import dotty.tools.dotc.core.Types.*
2627
import dotty.tools.dotc.interactive.Completion
28+
import dotty.tools.dotc.interactive.Completion.Mode
2729
import dotty.tools.dotc.transform.SymUtils.*
2830
import dotty.tools.dotc.util.SourcePosition
2931
import dotty.tools.dotc.util.Spans
@@ -54,6 +56,14 @@ class Completions(
5456

5557
val coursierComplete = new CoursierComplete(BuildInfo.scalaVersion)
5658

59+
private lazy val completionMode =
60+
val adjustedPath = Completion.pathBeforeDesugaring(path, pos)
61+
val mode = Completion.completionMode(adjustedPath, pos)
62+
path match
63+
case Literal(Constant(_: String)) :: _ => Mode.Term // literal completions
64+
case _ => mode
65+
66+
5767
private lazy val shouldAddSnippet =
5868
path match
5969
/* In case of `method@@()` we should not add snippets and the path
@@ -69,105 +79,33 @@ class Completions(
6979
case (_: Ident) :: (_: SeqLiteral) :: _ => false
7080
case _ => true
7181

72-
enum CursorPos:
73-
case Type(hasTypeParams: Boolean, hasNewKw: Boolean)
74-
case Term
75-
case Import
76-
77-
def include(sym: Symbol)(using Context): Boolean =
78-
val generalExclude =
79-
isUninterestingSymbol(sym) ||
80-
!isNotLocalForwardReference(sym) ||
81-
sym.isPackageObject
82-
83-
def isWildcardParam(sym: Symbol) =
84-
if sym.isTerm && sym.owner.isAnonymousFunction then
85-
sym.name match
86-
case DerivedName(under, _) =>
87-
under.isEmpty
88-
case _ => false
89-
else false
82+
private lazy val allowTemplateSuffix: Boolean =
83+
path match
84+
case _ :: New(selectOrIdent: (Select | Ident)) :: _ => true
85+
case _ => false
9086

91-
if generalExclude then false
92-
else
93-
this match
94-
case Type(_, _) => true
95-
case Term if isWildcardParam(sym) => false
96-
case Term if sym.isTerm || sym.is(Package) => true
97-
case Import => true
87+
def includeSymbol(sym: Symbol)(using Context): Boolean =
88+
val generalExclude =
89+
isUninterestingSymbol(sym) ||
90+
!isNotLocalForwardReference(sym) ||
91+
sym.isPackageObject
92+
93+
def isWildcardParam(sym: Symbol) =
94+
if sym.isTerm && sym.owner.isAnonymousFunction then
95+
sym.name match
96+
case DerivedName(under, _) =>
97+
under.isEmpty
9898
case _ => false
99-
end if
100-
end include
101-
102-
def allowBracketSuffix: Boolean =
103-
this match
104-
case Type(hasTypeParams, _) => !hasTypeParams
105-
case _ => false
106-
107-
def allowTemplateSuffix: Boolean =
108-
this match
109-
case Type(_, hasNewKw) => hasNewKw
110-
case _ => false
111-
112-
def allowApplicationSuffix: Boolean =
113-
this match
114-
case Term => true
115-
case _ => false
99+
else false
116100

117-
end CursorPos
101+
if generalExclude then false
102+
else if completionMode.is(Mode.ImportOrExport) then true
103+
else if completionMode.is(Mode.Term) && isWildcardParam(sym) then false
104+
else if completionMode.is(Mode.Term) && (sym.isTerm || sym.is(Package)) then true
105+
else !completionMode.is(Mode.Term)
106+
end if
107+
end includeSymbol
118108

119-
private lazy val cursorPos =
120-
calculateTypeInstanceAndNewPositions(Completion.pathBeforeDesugaring(path, pos))
121-
122-
private def calculateTypeInstanceAndNewPositions(
123-
path: List[Tree]
124-
): CursorPos =
125-
path match
126-
case (_: Import) :: _ => CursorPos.Import
127-
case _ :: (_: Import) :: _ => CursorPos.Import
128-
case (head: (Select | Ident)) :: tail =>
129-
// https://github.com/lampepfl/dotty/issues/15750
130-
// due to this issue in dotty, because of which trees after typer lose information,
131-
// we have to calculate hasNoSquareBracket manually:
132-
val hasSquareBracket =
133-
val span: Span = head.srcPos.span
134-
if span.exists then
135-
var i = span.end
136-
while i < (text.length() - 1) && text(i).isWhitespace do i = i + 1
137-
138-
if i < text.length() then text(i) == '['
139-
else false
140-
else false
141-
142-
def typePos = CursorPos.Type(hasSquareBracket, hasNewKw = false)
143-
def newTypePos =
144-
CursorPos.Type(hasSquareBracket, hasNewKw = true)
145-
146-
tail match
147-
case (v: ValOrDefDef) :: _ if v.tpt.sourcePos.contains(pos) =>
148-
typePos
149-
case New(selectOrIdent: (Select | Ident)) :: _
150-
if selectOrIdent.sourcePos.contains(pos) =>
151-
newTypePos
152-
case (a @ AppliedTypeTree(_, args)) :: _
153-
if args.exists(_.sourcePos.contains(pos)) =>
154-
typePos
155-
case (templ @ Template(constr, _, self, _)) :: _
156-
if (constr :: self :: templ.parents).exists(
157-
_.sourcePos.contains(pos)
158-
) =>
159-
typePos
160-
case _ =>
161-
CursorPos.Term
162-
end match
163-
164-
case (_: TypeTree) :: TypeApply(Select(newQualifier: New, _), _) :: _
165-
if newQualifier.sourcePos.contains(pos) =>
166-
CursorPos.Type(hasTypeParams = false, hasNewKw = true)
167-
168-
case _ => CursorPos.Term
169-
end match
170-
end calculateTypeInstanceAndNewPositions
171109

172110
def completions(): (List[CompletionValue], SymbolSearch.Result) =
173111
val (advanced, exclusive) = advancedCompletions(path, pos, completionPos)
@@ -198,7 +136,7 @@ class Completions(
198136
end match
199137

200138
val application = CompletionApplication.fromPath(path)
201-
val ordering = completionOrdering(application, cursorPos)
139+
val ordering = completionOrdering(application)
202140
val values = application.postProcess(all.sorted(ordering))
203141
(values, result)
204142
end completions
@@ -248,8 +186,7 @@ class Completions(
248186
private def findSuffix(symbol: Symbol): CompletionSuffix =
249187
CompletionSuffix.empty
250188
.chain { suffix => // for [] suffix
251-
if shouldAddSnippet &&
252-
cursorPos.allowBracketSuffix && symbol.info.typeParams.nonEmpty
189+
if shouldAddSnippet && symbol.info.typeParams.nonEmpty
253190
then
254191
val typeParamsCount = symbol.info.typeParams.length
255192
suffix.withNewSuffixSnippet(SuffixKind.Bracket(typeParamsCount))
@@ -279,7 +216,7 @@ class Completions(
279216
else suffix
280217
}
281218
.chain { suffix => // for {} suffix
282-
if shouldAddSnippet && cursorPos.allowTemplateSuffix
219+
if shouldAddSnippet && allowTemplateSuffix
283220
&& isAbstractType(symbol)
284221
then
285222
if suffix.hasSnippet then suffix.withNewSuffix(SuffixKind.Template)
@@ -301,7 +238,7 @@ class Completions(
301238
val methodSymbols =
302239
if shouldAddSnippet &&
303240
(sym.is(Flags.Module) || sym.isClass && !sym.is(Flags.Trait)) &&
304-
!sym.is(Flags.JavaDefined) && cursorPos.allowApplicationSuffix
241+
!sym.is(Flags.JavaDefined) && completionMode.is(Mode.Term)
305242
then
306243
val info =
307244
/* Companion will be added even for normal classes now,
@@ -627,7 +564,7 @@ class Completions(
627564
name.substring(0, name.length - 1)
628565
else name
629566
val id = nameId + symOnly.snippetSuffix.labelSnippet.getOrElse("")
630-
val include = cursorPos.include(sym)
567+
val include = includeSymbol(sym)
631568
(id, include)
632569
case kw: CompletionValue.Keyword => (kw.label, true)
633570
case mc: CompletionValue.MatchCompletion => (mc.label, true)
@@ -687,7 +624,6 @@ class Completions(
687624
private def computeRelevancePenalty(
688625
completion: CompletionValue,
689626
application: CompletionApplication,
690-
cursorPos: CursorPos,
691627
): Int =
692628
import scala.meta.internal.pc.MemberOrdering.*
693629

@@ -733,10 +669,8 @@ class Completions(
733669
relevance |= IsSynthetic
734670
if sym.isDeprecated then relevance |= IsDeprecated
735671
if isEvilMethod(sym.name) then relevance |= IsEvilMethod
736-
cursorPos match
737-
case CursorPos.Type(_, _) if !sym.isType =>
738-
relevance |= IsNotTypeInTypePos
739-
case _ =>
672+
if !completionMode.is(Mode.ImportOrExport) &&
673+
completionMode.is(Mode.Type) && !sym.isType then relevance |= IsNotTypeInTypePos
740674
relevance
741675
end symbolRelevance
742676

@@ -814,8 +748,7 @@ class Completions(
814748
end CompletionApplication
815749

816750
private def completionOrdering(
817-
application: CompletionApplication,
818-
cursorPos: CursorPos,
751+
application: CompletionApplication
819752
): Ordering[CompletionValue] =
820753
new Ordering[CompletionValue]:
821754
val queryLower = completionPos.query.toLowerCase()
@@ -830,8 +763,8 @@ class Completions(
830763

831764
def compareByRelevance(o1: CompletionValue, o2: CompletionValue): Int =
832765
Integer.compare(
833-
computeRelevancePenalty(o1, application, cursorPos),
834-
computeRelevancePenalty(o2, application, cursorPos),
766+
computeRelevancePenalty(o1, application),
767+
computeRelevancePenalty(o2, application),
835768
)
836769

837770
def fuzzyScore(o: CompletionValue.Symbolic): Int =

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -673,8 +673,8 @@ class CompletionSuite extends BaseCompletionSuite:
673673
|}
674674
|""".stripMargin,
675675
"""|Some[?] scala
676-
|Seq scala.collection.immutable
677-
|Set scala.collection.immutable
676+
|SafeVarargs java.lang
677+
|ScalaReflectionException scala
678678
|""".stripMargin,
679679
topLines = Some(3)
680680
)
@@ -709,8 +709,8 @@ class CompletionSuite extends BaseCompletionSuite:
709709
|}
710710
|""".stripMargin,
711711
"""|Number: Regex
712-
|Nil scala.collection.immutable
713-
|NoManifest scala.reflect
712+
|NegativeArraySizeException java.lang
713+
|NoClassDefFoundError java.lang
714714
|""".stripMargin,
715715
topLines = Option(3)
716716
)
@@ -724,8 +724,8 @@ class CompletionSuite extends BaseCompletionSuite:
724724
|}
725725
|""".stripMargin,
726726
"""|Number: Regex
727-
|Nil scala.collection.immutable
728-
|NoManifest scala.reflect
727+
|NegativeArraySizeException java.lang
728+
|NoClassDefFoundError java.lang
729729
|""".stripMargin,
730730
topLines = Option(3)
731731
)

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,6 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite:
293293
|""".stripMargin
294294
)
295295

296-
// Ignore for Scala 3, since we don't provide completions for null
297296
@Test def `match-typed` =
298297
checkEdit(
299298
"""|object Main {
@@ -305,11 +304,12 @@ class CompletionWorkspaceSuite extends BaseCompletionSuite:
305304
"""|import java.util.ArrayDeque
306305
|object Main {
307306
| def foo(): Unit = null match {
308-
| case x: ArrayDeque =>
307+
| case x: ArrayDeque[$0] =>
309308
| }
310309
|}
311310
|""".stripMargin,
312-
filter = _.contains("java.util")
311+
filter = _.contains("java.util"),
312+
assertSingleItem = false,
313313
)
314314

315315
@Test def `type` =

0 commit comments

Comments
 (0)