Skip to content

Commit

Permalink
Add tests, don't cache when CURSOR is added
Browse files Browse the repository at this point in the history
rochala committed Aug 27, 2024
1 parent 748d439 commit 7205f20
Showing 27 changed files with 256 additions and 94 deletions.
37 changes: 27 additions & 10 deletions compiler/src/dotty/tools/dotc/ast/NavigateAST.scala
Original file line number Diff line number Diff line change
@@ -75,24 +75,41 @@ object NavigateAST {
def pathTo(span: Span, from: List[Positioned], skipZeroExtent: Boolean = false)(using Context): List[Positioned] = {
def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = {
var bestFit: List[Positioned] = path
while (it.hasNext) {
val path1 = it.next() match {
// FIXME this has to be changed to deterministicaly find recoveed tree
case untpd.Select(qual, name) if name == StdNames.nme.??? => path
while (it.hasNext) do
val path1 = it.next() match
case sel: untpd.Select if isTreeFromRecovery(sel) => path
case p: Positioned if !p.isInstanceOf[Closure[?]] => singlePath(p, path)
case m: untpd.Modifiers => childPath(m.productIterator, path)
case xs: List[?] => childPath(xs.iterator, path)
case _ => path
}
if ((path1 ne path) &&
((bestFit eq path) ||
bestFit.head.span != path1.head.span &&
envelops(bestFit.head.span, path1.head.span)))

if (path1 ne path) && ((bestFit eq path) || isBetterFit(bestFit, path1)) then
bestFit = path1
}

bestFit
}

/**
* When choosing better fit we compare spans. If candidate span has starting or ending point inside (exclusive)
* current best fit it is selected as new best fit. This means that same spans are failing the first predicate.
*
* In case when spans start and end at same offsets we prefer non synthethic one.
*/
def isBetterFit(currentBest: List[Positioned], candidate: List[Positioned]): Boolean =
if currentBest.isEmpty && candidate.nonEmpty then true
else if currentBest.nonEmpty && candidate.nonEmpty then
val bestSpan= currentBest.head.span
val candidateSpan = candidate.head.span

bestSpan != candidateSpan &&
envelops(bestSpan, candidateSpan) ||
bestSpan.contains(candidateSpan) && bestSpan.isSynthetic && !candidateSpan.isSynthetic
else false


def isTreeFromRecovery(p: untpd.Select): Boolean =
p.name == StdNames.nme.??? && p.qualifier.symbol.name == StdNames.nme.Predef && p.span.isSynthetic

def envelops(a: Span, b: Span): Boolean =
!b.exists || a.exists && (
(a.start < b.start && a.end >= b.end ) || (a.start <= b.start && a.end > b.end)
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
@@ -406,7 +406,7 @@ object Parsers {
false
}

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

private var inFunReturnType = false
private def fromWithinReturnType[T](body: => T): T = {
Original file line number Diff line number Diff line change
@@ -1706,15 +1706,6 @@ class CompletionTest {
("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"),
))

@Test def testtest: Unit =
code"""|object M {
| def sel$m1
|}
|"""
.completion(m1, Set(
("getOrElse", Method, "[V1 >: String](key: Int, default: => V1): V1"),
))

@Test def noEnumCompletionInNewContext: Unit =
code"""|enum TestEnum:
| case TestCase
Original file line number Diff line number Diff line change
@@ -227,7 +227,7 @@ class HoverTest {
@Test def enums: Unit = {
code"""|package example
|enum TestEnum3:
| case ${m1}A${m2} // no tooltip
| case ${m1}A${m2} // no tooltip
|
|"""
.hover(m1 to m2, hoverContent("example.TestEnum3"))
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@ import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.interactive.Interactive
import dotty.tools.dotc.interactive.InteractiveDriver
import dotty.tools.dotc.util.SourceFile
import dotty.tools.pc.AutoImports.*
import dotty.tools.pc.completions.CompletionPos
import dotty.tools.pc.utils.InteractiveEnrichments.*

Original file line number Diff line number Diff line change
@@ -10,8 +10,8 @@ import dotty.tools.dotc.util.SourceFile
import scala.compiletime.uninitialized

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

@volatile private var lastCompiledURI: URI = uninitialized

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

end MetalsDriver
end CachingDriver
Original file line number Diff line number Diff line change
@@ -14,7 +14,6 @@ import scala.meta.internal.pc.LabelPart.*
import scala.meta.pc.InlayHintsParams
import scala.meta.pc.SymbolSearch

import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Flags
Original file line number Diff line number Diff line change
@@ -8,13 +8,14 @@ import scala.meta.internal.pc.CompilerAccess
import scala.meta.pc.PresentationCompilerConfig

import dotty.tools.dotc.reporting.StoreReporter
import dotty.tools.dotc.interactive.InteractiveDriver

class Scala3CompilerAccess(
config: PresentationCompilerConfig,
sh: Option[ScheduledExecutorService],
newCompiler: () => Scala3CompilerWrapper
)(using ec: ExecutionContextExecutor, rc: ReportContext)
extends CompilerAccess[StoreReporter, MetalsDriver](
extends CompilerAccess[StoreReporter, InteractiveDriver](
config,
sh,
newCompiler,
Original file line number Diff line number Diff line change
@@ -4,11 +4,12 @@ import scala.meta.internal.pc.CompilerWrapper
import scala.meta.internal.pc.ReporterAccess

import dotty.tools.dotc.reporting.StoreReporter
import dotty.tools.dotc.interactive.InteractiveDriver

class Scala3CompilerWrapper(driver: MetalsDriver)
extends CompilerWrapper[StoreReporter, MetalsDriver]:
class Scala3CompilerWrapper(driver: InteractiveDriver)
extends CompilerWrapper[StoreReporter, InteractiveDriver]:

override def compiler(): MetalsDriver = driver
override def compiler(): InteractiveDriver = driver

override def resetReporter(): Unit =
val ctx = driver.currentCtx
Original file line number Diff line number Diff line change
@@ -33,11 +33,13 @@ import dotty.tools.pc.completions.CompletionProvider
import dotty.tools.pc.InferExpectedType
import dotty.tools.pc.completions.OverrideCompletions
import dotty.tools.pc.buildinfo.BuildInfo
import dotty.tools.pc.SymbolInformationProvider
import dotty.tools.dotc.interactive.InteractiveDriver

import org.eclipse.lsp4j.DocumentHighlight
import org.eclipse.lsp4j.TextEdit
import org.eclipse.lsp4j as l
import dotty.tools.pc.SymbolInformationProvider


case class ScalaPresentationCompiler(
buildTargetIdentifier: String = "",
@@ -76,14 +78,20 @@ case class ScalaPresentationCompiler(
override def withReportsLoggerLevel(level: String): PresentationCompiler =
copy(reportsLevel = ReportLevel.fromString(level))

val compilerAccess: CompilerAccess[StoreReporter, MetalsDriver] =
val compilerAccess: CompilerAccess[StoreReporter, InteractiveDriver] =
Scala3CompilerAccess(
config,
sh,
() => new Scala3CompilerWrapper(newDriver)
)(using
ec
)
() => new Scala3CompilerWrapper(CachingDriver(driverSettings))
)(using ec)

val driverSettings =
val implicitSuggestionTimeout = List("-Ximport-suggestion-timeout", "0")
val defaultFlags = List("-color:never")
val filteredOptions = removeDoubleOptions(options.filterNot(forbiddenOptions))

filteredOptions ::: defaultFlags ::: implicitSuggestionTimeout ::: "-classpath" :: classpath
.mkString(File.pathSeparator) :: Nil

private def removeDoubleOptions(options: List[String]): List[String] =
options match
@@ -92,19 +100,6 @@ case class ScalaPresentationCompiler(
case head :: tail => head :: removeDoubleOptions(tail)
case Nil => options

def newDriver: MetalsDriver =
val implicitSuggestionTimeout = List("-Ximport-suggestion-timeout", "0")
val defaultFlags = List("-color:never")
val filteredOptions = removeDoubleOptions(
options.filterNot(forbiddenOptions)
)
val settings =
filteredOptions ::: defaultFlags ::: implicitSuggestionTimeout ::: "-classpath" :: classpath
.mkString(
File.pathSeparator
) :: Nil
new MetalsDriver(settings)

override def semanticTokens(
params: VirtualFileParams
): CompletableFuture[ju.List[Node]] =
@@ -146,6 +141,7 @@ case class ScalaPresentationCompiler(
new CompletionProvider(
search,
driver,
() => InteractiveDriver(driverSettings),
params,
config,
buildTargetIdentifier,
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dotty.tools.pc

import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.core.Comments.Comment

object ScriptFirstImportPosition:
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dotty.tools.pc

import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Symbols.*
Original file line number Diff line number Diff line change
@@ -7,8 +7,6 @@ import scala.meta.pc.PcSymbolKind
import scala.meta.pc.PcSymbolProperty

import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Denotations.Denotation
import dotty.tools.dotc.core.Denotations.MultiDenotation
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Names.*
import dotty.tools.dotc.core.StdNames.nme
@@ -19,6 +17,7 @@ import dotty.tools.pc.utils.InteractiveEnrichments.allSymbols
import dotty.tools.pc.utils.InteractiveEnrichments.stripBackticks
import scala.meta.internal.pc.PcSymbolInformation
import scala.meta.internal.pc.SymbolInfo
import dotty.tools.dotc.core.Denotations.{Denotation, MultiDenotation}

class SymbolInformationProvider(using Context):

Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@ import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Phases
import dotty.tools.dotc.core.StdNames.nme
import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Names.DerivedName
import dotty.tools.dotc.interactive.Interactive
import dotty.tools.dotc.interactive.Completion
import dotty.tools.dotc.interactive.InteractiveDriver
@@ -39,7 +40,8 @@ import scala.meta.pc.CompletionItemPriority

class CompletionProvider(
search: SymbolSearch,
driver: InteractiveDriver,
cachingDriver: InteractiveDriver,
freshDriver: () => InteractiveDriver,
params: OffsetParams,
config: PresentationCompilerConfig,
buildTargetIdentifier: String,
@@ -52,6 +54,16 @@ class CompletionProvider(

val (wasCursorApplied, code) = applyCompletionCursor(params)
val sourceFile = SourceFile.virtual(uri, code)

/** Creating a new fresh driver is way slower than reusing existing one,
* but runnig a compilation has side effects that modifies the state of the driver.
* We don't want to affect cachingDriver state with compilation including "CURSOR" suffix.
*
* We could in theory save this fresh driver for reuse, but it is a choice between extra memory usage and speed.
* The scenario in which "CURSOR" is applied (empty query or query equal to any keyword) has a slim chance of happening.
*/

val driver = if wasCursorApplied then freshDriver() else cachingDriver
driver.run(uri, sourceFile)

given ctx: Context = driver.currentCtx
@@ -63,11 +75,12 @@ class CompletionProvider(
val adjustedPath = Interactive.resolveTypedOrUntypedPath(tpdPath0, pos)(using newctx)

val tpdPath = tpdPath0 match
// $1$ // FIXME add check for a $1$ name to make sure we only do the below in lifting case
case Select(qual, name) :: tail if qual.symbol.is(Flags.Synthetic) =>
qual.symbol.defTree match
case valdef: ValDef => Select(valdef.rhs, name) :: tail
case _ => tpdPath0
case Select(qual, name) :: tail
// If for any reason we end up in param after lifting, we want to inline the synthetic val
if qual.symbol.is(Flags.Synthetic) && qual.symbol.name.isInstanceOf[DerivedName] =>
qual.symbol.defTree match
case valdef: ValDef => Select(valdef.rhs, name) :: tail
case _ => tpdPath0
case _ => tpdPath0


Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@ import java.nio.file.Path
import java.nio.file.Paths

import scala.collection.mutable
import scala.meta.internal.metals.Fuzzy
import scala.meta.internal.metals.ReportContext
import scala.meta.internal.mtags.CoursierComplete
import scala.meta.internal.pc.{IdentifierComparator, MemberOrdering, CompletionFuzzy}
@@ -27,15 +26,12 @@ import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.interactive.Completion
import dotty.tools.dotc.interactive.Completion.Mode
import dotty.tools.dotc.interactive.Interactive
import dotty.tools.dotc.util.SourcePosition
import dotty.tools.dotc.util.SrcPos
import dotty.tools.pc.AutoImports.AutoImportsGenerator
import dotty.tools.pc.buildinfo.BuildInfo
import dotty.tools.pc.completions.OverrideCompletions.OverrideExtractor
import dotty.tools.pc.utils.InteractiveEnrichments.*
import dotty.tools.dotc.core.Denotations.SingleDenotation
import dotty.tools.dotc.interactive.Interactive

class Completions(
text: String,
@@ -279,7 +275,6 @@ class Completions(
val affix = if methodDenot.symbol.isConstructor && existsApply then
adjustedPath match
case (select @ Select(qual, _)) :: _ =>
val start = qual.span.start
val insertRange = select.sourcePos.startPos.withEnd(completionPos.queryEnd).toLsp

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

val filteredSymbolicCompletions = symbolicCompletionsMap.filter: (name, denots) =>
val filteredSymbolicCompletions = symbolicCompletionsMap.filter: (name, _) =>
lazy val existsTypeWithoutSuffix: Boolean = !symbolicCompletionsMap
.get(name.toTypeName)
.forall(_.forall(sym => sym.snippetAffix.suffixes.nonEmpty))
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@ import dotty.tools.dotc.core.Types.TermRef
import dotty.tools.dotc.core.Types.Type
import dotty.tools.dotc.core.Types.TypeBounds
import dotty.tools.dotc.core.Types.WildcardType
import dotty.tools.dotc.util.SourcePosition
import dotty.tools.pc.IndexedContext
import dotty.tools.pc.utils.InteractiveEnrichments.*
import scala.annotation.tailrec
@@ -295,7 +294,6 @@ object NamedArgCompletions:
)
}

// FIXME pass query here
val prefix = ident
.map(_.name.toString)
.getOrElse("")
Original file line number Diff line number Diff line change
@@ -24,8 +24,6 @@ import dotty.tools.dotc.printing.RefinedPrinter
import dotty.tools.dotc.printing.Texts.Text
import dotty.tools.pc.AutoImports.AutoImportsGenerator
import dotty.tools.pc.AutoImports.ImportSel
import dotty.tools.pc.AutoImports.ImportSel.Direct
import dotty.tools.pc.AutoImports.ImportSel.Rename
import dotty.tools.pc.IndexedContext
import dotty.tools.pc.IndexedContext.Result
import dotty.tools.pc.Params
Original file line number Diff line number Diff line change
@@ -8,9 +8,7 @@ import scala.meta.internal.metals.CompilerRangeParams
import scala.language.unsafeNulls

import dotty.tools.pc.utils.TestInlayHints
import dotty.tools.pc.utils.TextEdits

import org.eclipse.lsp4j.TextEdit

class BaseInlayHintsSuite extends BasePCSuite {

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

}
}
Original file line number Diff line number Diff line change
@@ -13,22 +13,17 @@ class ReusableClassRunner(testClass: Class[BasePCSuite])
testClass.getDeclaredConstructor().newInstance()

override def createTest(): AnyRef = instance
override def withBefores(
method: FrameworkMethod,
target: Object,
statement: Statement
): Statement =
statement

override def withAfters(
method: FrameworkMethod,
target: Object,
statement: Statement
): Statement =
val newStatement = super.withAfters(method, target, statement)
new Statement():
override def evaluate(): Unit =
try
statement.evaluate()
newStatement.evaluate()
finally
if (isLastTestCase(method)) then instance.clean()

Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package dotty.tools.pc.tests

import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.pc.base.BasePCSuite
import dotty.tools.pc.ScalaPresentationCompiler
import org.junit.{Before, Test}

import scala.language.unsafeNulls
import scala.meta.internal.metals.EmptyCancelToken
import scala.meta.internal.metals.CompilerOffsetParams
import scala.meta.pc.OffsetParams
import scala.concurrent.Future
import scala.concurrent.Await
import scala.meta.pc.VirtualFileParams
import scala.concurrent.duration.*

import java.util.Collections
import java.nio.file.Paths
import java.util.concurrent.CompletableFuture


class CompilerCachingSuite extends BasePCSuite:

val timeout = 5.seconds

private def checkCompilationCount(params: VirtualFileParams, expected: Int): Unit =
presentationCompiler match
case pc: ScalaPresentationCompiler =>
val compilations= pc.compilerAccess.withNonInterruptableCompiler(Some(params))(-1, EmptyCancelToken) { driver =>
driver.compiler().currentCtx.runId
}.get(timeout.length, timeout.unit)
assertEquals(expected, compilations, s"Expected $expected compilations but got $compilations")
case _ => throw IllegalStateException("Presentation compiler should always be of type of ScalaPresentationCompiler")

private def getContext(params: VirtualFileParams): Context =
presentationCompiler match
case pc: ScalaPresentationCompiler =>
pc.compilerAccess.withNonInterruptableCompiler(Some(params))(null, EmptyCancelToken) { driver =>
driver.compiler().currentCtx
}.get(timeout.length, timeout.unit)
case _ => throw IllegalStateException("Presentation compiler should always be of type of ScalaPresentationCompiler")

@Before
def beforeEach: Unit =
presentationCompiler.restart()

// We want to run art least one compilation, so runId points at 3.
// This will ensure that we use the same driver, not recreate fresh one on each call
val dryRunParams = CompilerOffsetParams(Paths.get("Test.scala").toUri(), "dryRun", 1, EmptyCancelToken)
checkCompilationCount(dryRunParams, 2)
val freshContext = getContext(dryRunParams)
presentationCompiler.complete(dryRunParams).get(timeout.length, timeout.unit)
checkCompilationCount(dryRunParams, 3)
val dryRunContext = getContext(dryRunParams)
assert(freshContext != dryRunContext)


@Test
def `cursor-compilation-does-not-corrupt-cache`: Unit =

val fakeParamsCursor = CompilerOffsetParams(Paths.get("Test.scala").toUri(), "def hello = new", 15, EmptyCancelToken)
val fakeParams = CompilerOffsetParams(Paths.get("Test.scala").toUri(), "def hello = ne", 14, EmptyCancelToken)

val contextPreCompilation = getContext(fakeParams)

presentationCompiler.complete(fakeParams).get(timeout.length, timeout.unit)
val contextPostFirst = getContext(fakeParams)
assert(contextPreCompilation != contextPostFirst)
checkCompilationCount(fakeParams, 4)

presentationCompiler.complete(fakeParamsCursor).get(timeout.length, timeout.unit)
val contextPostCursor = getContext(fakeParamsCursor)
assert(contextPreCompilation != contextPostCursor)
assert(contextPostFirst == contextPostCursor)
checkCompilationCount(fakeParamsCursor, 4)

presentationCompiler.complete(fakeParams).get(timeout.length, timeout.unit)
val contextPostSecond = getContext(fakeParams)
assert(contextPreCompilation != contextPostSecond)
assert(contextPostFirst == contextPostCursor)
assert(contextPostCursor == contextPostSecond)
checkCompilationCount(fakeParamsCursor, 4)

@Test
def `compilation-for-same-snippet-is-cached`: Unit =
val fakeParams = CompilerOffsetParams(Paths.get("Test.scala").toUri(), "def hello = ne", 14, EmptyCancelToken)

val contextPreCompilation = getContext(fakeParams)

presentationCompiler.complete(fakeParams).get(timeout.length, timeout.unit)
val contextPostFirst = getContext(fakeParams)
assert(contextPreCompilation != contextPostFirst)
checkCompilationCount(fakeParams, 4)

presentationCompiler.complete(fakeParams).get(timeout.length, timeout.unit)
val contextPostSecond = getContext(fakeParams)
assert(contextPreCompilation != contextPostFirst)
assert(contextPostSecond == contextPostFirst)
checkCompilationCount(fakeParams, 4)

@Test
def `compilation-for-different-snippet-is-not-cached`: Unit =

val fakeParams = CompilerOffsetParams(Paths.get("Test.scala").toUri(), "def hello = prin", 16, EmptyCancelToken)
val fakeParams2 = CompilerOffsetParams(Paths.get("Test2.scala").toUri(), "def hello = prin", 16, EmptyCancelToken)
val fakeParams3 = CompilerOffsetParams(Paths.get("Test2.scala").toUri(), "def hello = print", 17, EmptyCancelToken)

checkCompilationCount(fakeParams, 3)
presentationCompiler.complete(fakeParams).get(timeout.length, timeout.unit)
checkCompilationCount(fakeParams, 4)

presentationCompiler.complete(fakeParams2).get(timeout.length, timeout.unit)
checkCompilationCount(fakeParams2, 5)

presentationCompiler.complete(fakeParams3).get(timeout.length, timeout.unit)
checkCompilationCount(fakeParams3, 6)


private val testFunctions: List[OffsetParams => CompletableFuture[_]] = List(
presentationCompiler.complete(_),
presentationCompiler.convertToNamedArguments(_, Collections.emptyList()),
presentationCompiler.autoImports("a", _, false),
presentationCompiler.definition(_),
presentationCompiler.didChange(_),
presentationCompiler.documentHighlight(_),
presentationCompiler.hover(_),
presentationCompiler.implementAbstractMembers(_),
presentationCompiler.insertInferredType(_),
presentationCompiler.semanticTokens(_),
presentationCompiler.prepareRename(_),
presentationCompiler.rename(_, "a"),
presentationCompiler.signatureHelp(_),
presentationCompiler.typeDefinition(_)
)


@Test
def `different-api-calls-reuse-cache`: Unit =
val fakeParams = CompilerOffsetParams(Paths.get("Test.scala").toUri(), "def hello = ne", 13, EmptyCancelToken)

presentationCompiler.complete(fakeParams).get(timeout.length, timeout.unit)
val contextBefore = getContext(fakeParams)

val differentContexts = testFunctions.map: f =>
f(fakeParams).get(timeout.length, timeout.unit)
checkCompilationCount(fakeParams, 4)
getContext(fakeParams)
.toSet

assert(differentContexts == Set(contextBefore))

@Test
def `different-api-calls-reuse-cache-parallel`: Unit =
import scala.jdk.FutureConverters.*
import scala.concurrent.ExecutionContext.Implicits.global

val fakeParams = CompilerOffsetParams(Paths.get("Test.scala").toUri(), "def hello = ne", 13, EmptyCancelToken)

presentationCompiler.complete(fakeParams).get(timeout.length, timeout.unit)
val contextBefore = getContext(fakeParams)

val futures = testFunctions.map: f =>
f(fakeParams).asScala.map(_ => getContext(fakeParams))

val res = Await.result(Future.sequence(futures), timeout).toSet
assert(res == Set(contextBefore))
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package dotty.tools.pc.tests.completion

import scala.meta.pc.SymbolDocumentation
import scala.language.unsafeNulls

import dotty.tools.pc.base.BaseCompletionSuite
import dotty.tools.pc.utils.MockEntries

import org.junit.Test
import org.junit.Ignore
import scala.collection.immutable.ListMapBuilder

class CompletionExtraConstructorSuite extends BaseCompletionSuite:

Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ class PcDefinitionSuite extends BasePcDefinitionSuite:
MockLocation("scala/Predef.Ensuring#ensuring(+2).", "Predef.scala"),
MockLocation("scala/Predef.Ensuring#ensuring(+3).", "Predef.scala"),
MockLocation("scala/collection/immutable/List#`::`().", "List.scala"),
MockLocation("scala/package.List.", "List.scala")
MockLocation("scala/package.List.", "package.scala")
)

override def definitions(offsetParams: OffsetParams): List[Location] =
@@ -123,7 +123,7 @@ class PcDefinitionSuite extends BasePcDefinitionSuite:
check(
"""|
|object Main {
| /*scala/package.List. List.scala*/@@List(1)
| /*scala/package.List. package.scala*/@@List(1)
|}
|""".stripMargin
)
Original file line number Diff line number Diff line change
@@ -694,3 +694,11 @@ class HoverTermSuite extends BaseHoverSuite:
|""".stripMargin,
"extension [T](a: T) def *:[U <: Tuple](b: Wrap[U]): Wrap[T *: U]".hover
)

@Test def `dont-ignore-???-in-path`: Unit =
check(
"""object Obj:
| val x = ?@@??
|""".stripMargin,
"""def ???: Nothing""".stripMargin.hover
)
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ package dotty.tools.pc.utils
import scala.meta.pc.VirtualFileParams

import dotty.tools.dotc.ast.tpd.*
import dotty.tools.dotc.ast.{Trees, tpd}
import dotty.tools.dotc.ast.Trees
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.interactive.InteractiveDriver
import dotty.tools.dotc.util.SourcePosition
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ import scala.jdk.CollectionConverters.*
import scala.jdk.OptionConverters.*
import scala.meta.internal.metals.{ClasspathSearch, WorkspaceSymbolQuery}
import scala.meta.pc.ContentType
import scala.meta.pc.SymbolSearch.Result
import scala.meta.pc.{
ParentSymbols,
SymbolDocumentation,
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ import scala.language.unsafeNulls

import dotty.tools.pc.completions.CompletionSource
import dotty.tools.dotc.util.DiffUtil
import dotty.tools.pc.utils.InteractiveEnrichments.*

import org.hamcrest
import org.hamcrest.*
@@ -127,7 +126,6 @@ trait PcAssertions:
def getDetailedMessage(diff: String): String =
val lines = diff.linesIterator.toList
val sources = completionSources.padTo(lines.size, CompletionSource.Empty)
val maxLength = lines.map(_.length).maxOption.getOrElse(0)
var completionIndex = 0
lines.map: line =>
if line.startsWith(Console.BOLD + Console.RED) || line.startsWith(" ") then
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ import scala.collection.mutable.ListBuffer

import scala.meta.internal.jdk.CollectionConverters._
import dotty.tools.pc.utils.InteractiveEnrichments.*
import dotty.tools.pc.utils.TextEdits

import org.eclipse.lsp4j.InlayHint
import org.eclipse.lsp4j.TextEdit
@@ -67,4 +66,4 @@ object TestInlayHints {
def removeInlayHints(text: String): String =
text.replaceAll(raw"\/\*(.*?)\*\/", "").nn

}
}

0 comments on commit 7205f20

Please sign in to comment.