Skip to content

Commit 97f5f1c

Browse files
committed
refactor cursorPos to reuse compiler completion mode
1 parent a30971a commit 97f5f1c

File tree

4 files changed

+60
-127
lines changed

4 files changed

+60
-127
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: 50 additions & 117 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,113 +79,41 @@ 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-
def hasSyntheticCursorSuffix: Boolean =
79-
if !sym.name.endsWith(Cursor.value) then false
80-
else
81-
val realNameLength = sym.decodedName.length - Cursor.value.length
82-
sym.source == pos.source &&
83-
sym.span.start + realNameLength == pos.span.end
84-
85-
val generalExclude =
86-
isUninterestingSymbol(sym) ||
87-
!isNotLocalForwardReference(sym) ||
88-
sym.isPackageObject ||
89-
hasSyntheticCursorSuffix
90-
91-
def isWildcardParam(sym: Symbol) =
92-
if sym.isTerm && sym.owner.isAnonymousFunction then
93-
sym.name match
94-
case DerivedName(under, _) =>
95-
under.isEmpty
96-
case _ => false
97-
else false
82+
private lazy val allowTemplateSuffix: Boolean =
83+
path match
84+
case _ :: New(selectOrIdent: (Select | Ident)) :: _ => true
85+
case _ => false
9886

99-
if generalExclude then false
87+
def includeSymbol(sym: Symbol)(using Context): Boolean =
88+
def hasSyntheticCursorSuffix: Boolean =
89+
if !sym.name.endsWith(Cursor.value) then false
10090
else
101-
this match
102-
case Type(_, _) => true
103-
case Term if isWildcardParam(sym) => false
104-
case Term if sym.isTerm || sym.is(Package) => true
105-
case Import => true
91+
val realNameLength = sym.decodedName.length - Cursor.value.length
92+
sym.source == pos.source &&
93+
sym.span.start + realNameLength == pos.span.end
94+
95+
val generalExclude =
96+
isUninterestingSymbol(sym) ||
97+
!isNotLocalForwardReference(sym) ||
98+
sym.isPackageObject ||
99+
hasSyntheticCursorSuffix
100+
101+
def isWildcardParam(sym: Symbol) =
102+
if sym.isTerm && sym.owner.isAnonymousFunction then
103+
sym.name match
104+
case DerivedName(under, _) =>
105+
under.isEmpty
106106
case _ => false
107-
end if
108-
end include
109-
110-
def allowBracketSuffix: Boolean =
111-
this match
112-
case Type(hasTypeParams, _) => !hasTypeParams
113-
case _ => false
114-
115-
def allowTemplateSuffix: Boolean =
116-
this match
117-
case Type(_, hasNewKw) => hasNewKw
118-
case _ => false
107+
else false
119108

120-
def allowApplicationSuffix: Boolean =
121-
this match
122-
case Term => true
123-
case _ => false
109+
if generalExclude then false
110+
else if completionMode.is(Mode.ImportOrExport) then true
111+
else if completionMode.is(Mode.Term) && isWildcardParam(sym) then false
112+
else if completionMode.is(Mode.Term) && (sym.isTerm || sym.is(Package)) then true
113+
else !completionMode.is(Mode.Term)
114+
end if
115+
end includeSymbol
124116

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

180118
def completions(): (List[CompletionValue], SymbolSearch.Result) =
181119
val (advanced, exclusive) = advancedCompletions(path, pos, completionPos)
@@ -206,7 +144,7 @@ class Completions(
206144
end match
207145

208146
val application = CompletionApplication.fromPath(path)
209-
val ordering = completionOrdering(application, cursorPos)
147+
val ordering = completionOrdering(application)
210148
val values = application.postProcess(all.sorted(ordering))
211149
(values, result)
212150
end completions
@@ -256,8 +194,7 @@ class Completions(
256194
private def findSuffix(symbol: Symbol): CompletionSuffix =
257195
CompletionSuffix.empty
258196
.chain { suffix => // for [] suffix
259-
if shouldAddSnippet &&
260-
cursorPos.allowBracketSuffix && symbol.info.typeParams.nonEmpty
197+
if shouldAddSnippet && symbol.info.typeParams.nonEmpty
261198
then suffix.withNewSuffixSnippet(SuffixKind.Bracket)
262199
else suffix
263200
}
@@ -285,7 +222,7 @@ class Completions(
285222
else suffix
286223
}
287224
.chain { suffix => // for {} suffix
288-
if shouldAddSnippet && cursorPos.allowTemplateSuffix
225+
if shouldAddSnippet && allowTemplateSuffix
289226
&& isAbstractType(symbol)
290227
then
291228
if suffix.hasSnippet then suffix.withNewSuffix(SuffixKind.Template)
@@ -307,7 +244,7 @@ class Completions(
307244
val methodSymbols =
308245
if shouldAddSnippet &&
309246
(sym.is(Flags.Module) || sym.isClass && !sym.is(Flags.Trait)) &&
310-
!sym.is(Flags.JavaDefined) && cursorPos.allowApplicationSuffix
247+
!sym.is(Flags.JavaDefined) && completionMode.is(Mode.Term)
311248
then
312249
val info =
313250
/* Companion will be added even for normal classes now,
@@ -635,7 +572,7 @@ class Completions(
635572
val suffix =
636573
if symOnly.snippetSuffix.addLabelSnippet then "[]" else ""
637574
val id = nameId + suffix
638-
val include = cursorPos.include(sym)
575+
val include = includeSymbol(sym)
639576
(id, include)
640577
case kw: CompletionValue.Keyword => (kw.label, true)
641578
case mc: CompletionValue.MatchCompletion => (mc.label, true)
@@ -695,7 +632,6 @@ class Completions(
695632
private def computeRelevancePenalty(
696633
completion: CompletionValue,
697634
application: CompletionApplication,
698-
cursorPos: CursorPos,
699635
): Int =
700636
import scala.meta.internal.pc.MemberOrdering.*
701637

@@ -741,10 +677,8 @@ class Completions(
741677
relevance |= IsSynthetic
742678
if sym.isDeprecated then relevance |= IsDeprecated
743679
if isEvilMethod(sym.name) then relevance |= IsEvilMethod
744-
cursorPos match
745-
case CursorPos.Type(_, _) if !sym.isType =>
746-
relevance |= IsNotTypeInTypePos
747-
case _ =>
680+
if !completionMode.is(Mode.ImportOrExport) &&
681+
completionMode.is(Mode.Type) && !sym.isType then relevance |= IsNotTypeInTypePos
748682
relevance
749683
end symbolRelevance
750684

@@ -822,8 +756,7 @@ class Completions(
822756
end CompletionApplication
823757

824758
private def completionOrdering(
825-
application: CompletionApplication,
826-
cursorPos: CursorPos,
759+
application: CompletionApplication
827760
): Ordering[CompletionValue] =
828761
new Ordering[CompletionValue]:
829762
val queryLower = completionPos.query.toLowerCase()
@@ -838,8 +771,8 @@ class Completions(
838771

839772
def compareByRelevance(o1: CompletionValue, o2: CompletionValue): Int =
840773
Integer.compare(
841-
computeRelevancePenalty(o1, application, cursorPos),
842-
computeRelevancePenalty(o2, application, cursorPos),
774+
computeRelevancePenalty(o1, application),
775+
computeRelevancePenalty(o2, application),
843776
)
844777

845778
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)