Skip to content

Commit cddbc28

Browse files
committed
Add tests, don't cache when CURSOR is added
1 parent 562b66d commit cddbc28

27 files changed

+256
-94
lines changed

compiler/src/dotty/tools/dotc/ast/NavigateAST.scala

+27-10
Original file line numberDiff line numberDiff line change
@@ -75,24 +75,41 @@ object NavigateAST {
7575
def pathTo(span: Span, from: List[Positioned], skipZeroExtent: Boolean = false)(using Context): List[Positioned] = {
7676
def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = {
7777
var bestFit: List[Positioned] = path
78-
while (it.hasNext) {
79-
val path1 = it.next() match {
80-
// FIXME this has to be changed to deterministicaly find recoveed tree
81-
case untpd.Select(qual, name) if name == StdNames.nme.??? => path
78+
while (it.hasNext) do
79+
val path1 = it.next() match
80+
case sel: untpd.Select if isTreeFromRecovery(sel) => path
8281
case p: Positioned if !p.isInstanceOf[Closure[?]] => singlePath(p, path)
8382
case m: untpd.Modifiers => childPath(m.productIterator, path)
8483
case xs: List[?] => childPath(xs.iterator, path)
8584
case _ => path
86-
}
87-
if ((path1 ne path) &&
88-
((bestFit eq path) ||
89-
bestFit.head.span != path1.head.span &&
90-
envelops(bestFit.head.span, path1.head.span)))
85+
86+
if (path1 ne path) && ((bestFit eq path) || isBetterFit(bestFit, path1)) then
9187
bestFit = path1
92-
}
88+
9389
bestFit
9490
}
9591

92+
/**
93+
* When choosing better fit we compare spans. If candidate span has starting or ending point inside (exclusive)
94+
* current best fit it is selected as new best fit. This means that same spans are failing the first predicate.
95+
*
96+
* In case when spans start and end at same offsets we prefer non synthethic one.
97+
*/
98+
def isBetterFit(currentBest: List[Positioned], candidate: List[Positioned]): Boolean =
99+
if currentBest.isEmpty && candidate.nonEmpty then true
100+
else if currentBest.nonEmpty && candidate.nonEmpty then
101+
val bestSpan= currentBest.head.span
102+
val candidateSpan = candidate.head.span
103+
104+
bestSpan != candidateSpan &&
105+
envelops(bestSpan, candidateSpan) ||
106+
bestSpan.contains(candidateSpan) && bestSpan.isSynthetic && !candidateSpan.isSynthetic
107+
else false
108+
109+
110+
def isTreeFromRecovery(p: untpd.Select): Boolean =
111+
p.name == StdNames.nme.??? && p.qualifier.symbol.name == StdNames.nme.Predef && p.span.isSynthetic
112+
96113
def envelops(a: Span, b: Span): Boolean =
97114
!b.exists || a.exists && (
98115
(a.start < b.start && a.end >= b.end ) || (a.start <= b.start && a.end > b.end)

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ object Parsers {
402402
false
403403
}
404404

405-
def errorTermTree(start: Offset): Tree = atSpan(start, in.offset, in.offset) { unimplementedExpr }
405+
def errorTermTree(start: Offset): Tree = atSpan(Span(start, in.offset)) { unimplementedExpr }
406406

407407
private var inFunReturnType = false
408408
private def fromWithinReturnType[T](body: => T): T = {

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

-9
Original file line numberDiff line numberDiff line change
@@ -1705,15 +1705,6 @@ class CompletionTest {
17051705
("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"),
17061706
))
17071707

1708-
@Test def testtest: Unit =
1709-
code"""|object M {
1710-
| def sel$m1
1711-
|}
1712-
|"""
1713-
.completion(m1, Set(
1714-
("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"),
1715-
))
1716-
17171708
@Test def noEnumCompletionInNewContext: Unit =
17181709
code"""|enum TestEnum:
17191710
| case TestCase

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ class HoverTest {
227227
@Test def enums: Unit = {
228228
code"""|package example
229229
|enum TestEnum3:
230-
| case ${m1}A${m2} // no tooltip
230+
| case ${m1}A${m2} // no tooltip
231231
|
232232
|"""
233233
.hover(m1 to m2, hoverContent("example.TestEnum3"))

presentation-compiler/src/main/dotty/tools/pc/AutoImportsProvider.scala

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import dotty.tools.dotc.core.Symbols.*
1313
import dotty.tools.dotc.interactive.Interactive
1414
import dotty.tools.dotc.interactive.InteractiveDriver
1515
import dotty.tools.dotc.util.SourceFile
16-
import dotty.tools.pc.AutoImports.*
1716
import dotty.tools.pc.completions.CompletionPos
1817
import dotty.tools.pc.utils.InteractiveEnrichments.*
1918

presentation-compiler/src/main/dotty/tools/pc/MetalsDriver.scala renamed to presentation-compiler/src/main/dotty/tools/pc/CachingDriver.scala

+4-6
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import dotty.tools.dotc.util.SourceFile
1010
import scala.compiletime.uninitialized
1111

1212
/**
13-
* MetalsDriver is a wrapper class that provides a compilation cache for InteractiveDriver.
14-
* MetalsDriver skips running compilation if
13+
* CachingDriver is a wrapper class that provides a compilation cache for InteractiveDriver.
14+
* CachingDriver skips running compilation if
1515
* - the target URI of `run` is the same as the previous target URI
1616
* - the content didn't change since the last compilation.
1717
*
@@ -27,9 +27,7 @@ import scala.compiletime.uninitialized
2727
* To avoid the complexity related to currentCtx,
2828
* we decided to cache only when the target URI only if the same as the previous run.
2929
*/
30-
class MetalsDriver(
31-
override val settings: List[String]
32-
) extends InteractiveDriver(settings):
30+
class CachingDriver(override val settings: List[String]) extends InteractiveDriver(settings):
3331

3432
@volatile private var lastCompiledURI: URI = uninitialized
3533

@@ -55,4 +53,4 @@ class MetalsDriver(
5553
lastCompiledURI = uri
5654
diags
5755

58-
end MetalsDriver
56+
end CachingDriver

presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala

-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import scala.meta.internal.pc.LabelPart.*
1414
import scala.meta.pc.InlayHintsParams
1515
import scala.meta.pc.SymbolSearch
1616

17-
import dotty.tools.dotc.ast.tpd
1817
import dotty.tools.dotc.ast.tpd.*
1918
import dotty.tools.dotc.core.Contexts.Context
2019
import dotty.tools.dotc.core.Flags

presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerAccess.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ import scala.meta.internal.pc.CompilerAccess
88
import scala.meta.pc.PresentationCompilerConfig
99

1010
import dotty.tools.dotc.reporting.StoreReporter
11+
import dotty.tools.dotc.interactive.InteractiveDriver
1112

1213
class Scala3CompilerAccess(
1314
config: PresentationCompilerConfig,
1415
sh: Option[ScheduledExecutorService],
1516
newCompiler: () => Scala3CompilerWrapper
1617
)(using ec: ExecutionContextExecutor, rc: ReportContext)
17-
extends CompilerAccess[StoreReporter, MetalsDriver](
18+
extends CompilerAccess[StoreReporter, InteractiveDriver](
1819
config,
1920
sh,
2021
newCompiler,

presentation-compiler/src/main/dotty/tools/pc/Scala3CompilerWrapper.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import scala.meta.internal.pc.CompilerWrapper
44
import scala.meta.internal.pc.ReporterAccess
55

66
import dotty.tools.dotc.reporting.StoreReporter
7+
import dotty.tools.dotc.interactive.InteractiveDriver
78

8-
class Scala3CompilerWrapper(driver: MetalsDriver)
9-
extends CompilerWrapper[StoreReporter, MetalsDriver]:
9+
class Scala3CompilerWrapper(driver: InteractiveDriver)
10+
extends CompilerWrapper[StoreReporter, InteractiveDriver]:
1011

11-
override def compiler(): MetalsDriver = driver
12+
override def compiler(): InteractiveDriver = driver
1213

1314
override def resetReporter(): Unit =
1415
val ctx = driver.currentCtx

presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala

+15-19
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@ import dotty.tools.dotc.reporting.StoreReporter
3232
import dotty.tools.pc.completions.CompletionProvider
3333
import dotty.tools.pc.completions.OverrideCompletions
3434
import dotty.tools.pc.buildinfo.BuildInfo
35+
import dotty.tools.pc.SymbolInformationProvider
36+
import dotty.tools.dotc.interactive.InteractiveDriver
3537

3638
import org.eclipse.lsp4j.DocumentHighlight
3739
import org.eclipse.lsp4j.TextEdit
3840
import org.eclipse.lsp4j as l
39-
import dotty.tools.pc.SymbolInformationProvider
41+
4042

4143
case class ScalaPresentationCompiler(
4244
buildTargetIdentifier: String = "",
@@ -69,14 +71,20 @@ case class ScalaPresentationCompiler(
6971
override def withReportsLoggerLevel(level: String): PresentationCompiler =
7072
copy(reportsLevel = ReportLevel.fromString(level))
7173

72-
val compilerAccess: CompilerAccess[StoreReporter, MetalsDriver] =
74+
val compilerAccess: CompilerAccess[StoreReporter, InteractiveDriver] =
7375
Scala3CompilerAccess(
7476
config,
7577
sh,
76-
() => new Scala3CompilerWrapper(newDriver)
77-
)(using
78-
ec
79-
)
78+
() => new Scala3CompilerWrapper(CachingDriver(driverSettings))
79+
)(using ec)
80+
81+
val driverSettings =
82+
val implicitSuggestionTimeout = List("-Ximport-suggestion-timeout", "0")
83+
val defaultFlags = List("-color:never")
84+
val filteredOptions = removeDoubleOptions(options.filterNot(forbiddenOptions))
85+
86+
filteredOptions ::: defaultFlags ::: implicitSuggestionTimeout ::: "-classpath" :: classpath
87+
.mkString(File.pathSeparator) :: Nil
8088

8189
private def removeDoubleOptions(options: List[String]): List[String] =
8290
options match
@@ -85,19 +93,6 @@ case class ScalaPresentationCompiler(
8593
case head :: tail => head :: removeDoubleOptions(tail)
8694
case Nil => options
8795

88-
def newDriver: MetalsDriver =
89-
val implicitSuggestionTimeout = List("-Ximport-suggestion-timeout", "0")
90-
val defaultFlags = List("-color:never")
91-
val filteredOptions = removeDoubleOptions(
92-
options.filterNot(forbiddenOptions)
93-
)
94-
val settings =
95-
filteredOptions ::: defaultFlags ::: implicitSuggestionTimeout ::: "-classpath" :: classpath
96-
.mkString(
97-
File.pathSeparator
98-
) :: Nil
99-
new MetalsDriver(settings)
100-
10196
override def semanticTokens(
10297
params: VirtualFileParams
10398
): CompletableFuture[ju.List[Node]] =
@@ -139,6 +134,7 @@ case class ScalaPresentationCompiler(
139134
new CompletionProvider(
140135
search,
141136
driver,
137+
() => InteractiveDriver(driverSettings),
142138
params,
143139
config,
144140
buildTargetIdentifier,

presentation-compiler/src/main/dotty/tools/pc/ScriptFirstImportPosition.scala

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dotty.tools.pc
22

3-
import dotty.tools.dotc.ast.tpd.*
43
import dotty.tools.dotc.core.Comments.Comment
54

65
object ScriptFirstImportPosition:

presentation-compiler/src/main/dotty/tools/pc/SignatureHelpProvider.scala

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dotty.tools.pc
22

3-
import dotty.tools.dotc.ast.tpd.*
43
import dotty.tools.dotc.core.Contexts.*
54
import dotty.tools.dotc.core.Flags
65
import dotty.tools.dotc.core.Symbols.*

presentation-compiler/src/main/dotty/tools/pc/SymbolInformationProvider.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import scala.meta.pc.PcSymbolKind
66
import scala.meta.pc.PcSymbolProperty
77

88
import dotty.tools.dotc.core.Contexts.Context
9-
import dotty.tools.dotc.core.Denotations.Denotation
10-
import dotty.tools.dotc.core.Denotations.MultiDenotation
119
import dotty.tools.dotc.core.Flags
1210
import dotty.tools.dotc.core.Names.*
1311
import dotty.tools.dotc.core.StdNames.nme
@@ -18,6 +16,7 @@ import dotty.tools.pc.utils.InteractiveEnrichments.allSymbols
1816
import dotty.tools.pc.utils.InteractiveEnrichments.stripBackticks
1917
import scala.meta.internal.pc.PcSymbolInformation
2018
import scala.meta.internal.pc.SymbolInfo
19+
import dotty.tools.dotc.core.Denotations.{Denotation, MultiDenotation}
2120

2221
class SymbolInformationProvider(using Context):
2322

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

+19-6
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import dotty.tools.dotc.core.Contexts.Context
1616
import dotty.tools.dotc.core.Phases
1717
import dotty.tools.dotc.core.StdNames.nme
1818
import dotty.tools.dotc.core.Flags
19+
import dotty.tools.dotc.core.Names.DerivedName
1920
import dotty.tools.dotc.interactive.Interactive
2021
import dotty.tools.dotc.interactive.Completion
2122
import dotty.tools.dotc.interactive.InteractiveDriver
@@ -38,7 +39,8 @@ import org.eclipse.lsp4j.TextEdit
3839

3940
class CompletionProvider(
4041
search: SymbolSearch,
41-
driver: InteractiveDriver,
42+
cachingDriver: InteractiveDriver,
43+
freshDriver: () => InteractiveDriver,
4244
params: OffsetParams,
4345
config: PresentationCompilerConfig,
4446
buildTargetIdentifier: String,
@@ -50,6 +52,16 @@ class CompletionProvider(
5052

5153
val (wasCursorApplied, code) = applyCompletionCursor(params)
5254
val sourceFile = SourceFile.virtual(uri, code)
55+
56+
/** Creating a new fresh driver is way slower than reusing existing one,
57+
* but runnig a compilation has side effects that modifies the state of the driver.
58+
* We don't want to affect cachingDriver state with compilation including "CURSOR" suffix.
59+
*
60+
* We could in theory save this fresh driver for reuse, but it is a choice between extra memory usage and speed.
61+
* The scenario in which "CURSOR" is applied (empty query or query equal to any keyword) has a slim chance of happening.
62+
*/
63+
64+
val driver = if wasCursorApplied then freshDriver() else cachingDriver
5365
driver.run(uri, sourceFile)
5466

5567
given ctx: Context = driver.currentCtx
@@ -61,11 +73,12 @@ class CompletionProvider(
6173
val adjustedPath = Interactive.resolveTypedOrUntypedPath(tpdPath0, pos)(using newctx)
6274

6375
val tpdPath = tpdPath0 match
64-
// $1$ // FIXME add check for a $1$ name to make sure we only do the below in lifting case
65-
case Select(qual, name) :: tail if qual.symbol.is(Flags.Synthetic) =>
66-
qual.symbol.defTree match
67-
case valdef: ValDef => Select(valdef.rhs, name) :: tail
68-
case _ => tpdPath0
76+
case Select(qual, name) :: tail
77+
// If for any reason we end up in param after lifting, we want to inline the synthetic val
78+
if qual.symbol.is(Flags.Synthetic) && qual.symbol.name.isInstanceOf[DerivedName] =>
79+
qual.symbol.defTree match
80+
case valdef: ValDef => Select(valdef.rhs, name) :: tail
81+
case _ => tpdPath0
6982
case _ => tpdPath0
7083

7184

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

+1-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import java.nio.file.Path
55
import java.nio.file.Paths
66

77
import scala.collection.mutable
8-
import scala.meta.internal.metals.Fuzzy
98
import scala.meta.internal.metals.ReportContext
109
import scala.meta.internal.mtags.CoursierComplete
1110
import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering, CompletionFuzzy}
@@ -27,15 +26,12 @@ import dotty.tools.dotc.core.Symbols.*
2726
import dotty.tools.dotc.core.Types.*
2827
import dotty.tools.dotc.interactive.Completion
2928
import dotty.tools.dotc.interactive.Completion.Mode
30-
import dotty.tools.dotc.interactive.Interactive
3129
import dotty.tools.dotc.util.SourcePosition
3230
import dotty.tools.dotc.util.SrcPos
3331
import dotty.tools.pc.AutoImports.AutoImportsGenerator
3432
import dotty.tools.pc.buildinfo.BuildInfo
3533
import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor
3634
import dotty.tools.pc.utils.InteractiveEnrichments.*
37-
import dotty.tools.dotc.core.Denotations.SingleDenotation
38-
import dotty.tools.dotc.interactive.Interactive
3935

4036
class Completions(
4137
text: String,
@@ -271,7 +267,6 @@ class Completions(
271267
val affix = if methodDenot.symbol.isConstructor && existsApply then
272268
adjustedPath match
273269
case (select @ Select(qual, _)) :: _ =>
274-
val start = qual.span.start
275270
val insertRange = select.sourcePos.startPos.withEnd(completionPos.queryEnd).toLsp
276271

277272
suffix
@@ -651,7 +646,7 @@ class Completions(
651646
.collect { case symbolic: CompletionValue.Symbolic => symbolic }
652647
.groupBy(_.symbol.fullName) // we somehow have to ignore proxy type
653648

654-
val filteredSymbolicCompletions = symbolicCompletionsMap.filter: (name, denots) =>
649+
val filteredSymbolicCompletions = symbolicCompletionsMap.filter: (name, _) =>
655650
lazy val existsTypeWithoutSuffix: Boolean = !symbolicCompletionsMap
656651
.get(name.toTypeName)
657652
.forall(_.forall(sym => sym.snippetAffix.suffixes.nonEmpty))

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

-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import dotty.tools.dotc.core.Types.TermRef
2727
import dotty.tools.dotc.core.Types.Type
2828
import dotty.tools.dotc.core.Types.TypeBounds
2929
import dotty.tools.dotc.core.Types.WildcardType
30-
import dotty.tools.dotc.util.SourcePosition
3130
import dotty.tools.pc.IndexedContext
3231
import dotty.tools.pc.utils.InteractiveEnrichments.*
3332
import scala.annotation.tailrec
@@ -295,7 +294,6 @@ object NamedArgCompletions:
295294
)
296295
}
297296

298-
// FIXME pass query here
299297
val prefix = ident
300298
.map(_.name.toString)
301299
.getOrElse("")

presentation-compiler/src/main/dotty/tools/pc/printer/ShortenedTypePrinter.scala

-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ import dotty.tools.dotc.printing.RefinedPrinter
2424
import dotty.tools.dotc.printing.Texts.Text
2525
import dotty.tools.pc.AutoImports.AutoImportsGenerator
2626
import dotty.tools.pc.AutoImports.ImportSel
27-
import dotty.tools.pc.AutoImports.ImportSel.Direct
28-
import dotty.tools.pc.AutoImports.ImportSel.Rename
2927
import dotty.tools.pc.IndexedContext
3028
import dotty.tools.pc.IndexedContext.Result
3129
import dotty.tools.pc.Params

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

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ import scala.meta.internal.metals.CompilerRangeParams
88
import scala.language.unsafeNulls
99

1010
import dotty.tools.pc.utils.TestInlayHints
11-
import dotty.tools.pc.utils.TextEdits
1211

13-
import org.eclipse.lsp4j.TextEdit
1412

1513
class BaseInlayHintsSuite extends BasePCSuite {
1614

@@ -55,4 +53,4 @@ class BaseInlayHintsSuite extends BasePCSuite {
5553
obtained,
5654
)
5755

58-
}
56+
}

0 commit comments

Comments
 (0)