diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala index 7d8e4dfad081..8ff43ba07358 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcDefinitionProvider.scala @@ -1,5 +1,6 @@ package dotty.tools.pc +import java.net.URI import java.nio.file.Paths import java.util.ArrayList @@ -16,6 +17,7 @@ import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Flags.{Exported, ModuleClass} import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.interactive.Interactive +import dotty.tools.dotc.interactive.Interactive.Include import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.util.SourceFile import dotty.tools.dotc.util.SourcePosition @@ -51,10 +53,10 @@ class PcDefinitionProvider( given ctx: Context = driver.localContext(params) val indexedContext = IndexedContext(ctx) val result = - if findTypeDef then findTypeDefinitions(path, pos, indexedContext) - else findDefinitions(path, pos, indexedContext) + if findTypeDef then findTypeDefinitions(path, pos, indexedContext, uri) + else findDefinitions(path, pos, indexedContext, uri) - if result.locations().nn.isEmpty() then fallbackToUntyped(pos)(using ctx) + if result.locations().nn.isEmpty() then fallbackToUntyped(pos, uri)(using ctx) else result end definitions @@ -70,24 +72,26 @@ class PcDefinitionProvider( * @param pos cursor position * @return definition result */ - private def fallbackToUntyped(pos: SourcePosition)( + private def fallbackToUntyped(pos: SourcePosition, uri: URI)( using ctx: Context ) = lazy val untpdPath = NavigateAST .untypedPath(pos.span) .collect { case t: untpd.Tree => t } - definitionsForSymbol(untpdPath.headOption.map(_.symbol).toList, pos) + definitionsForSymbol(untpdPath.headOption.map(_.symbol).toList, uri, pos) end fallbackToUntyped private def findDefinitions( path: List[Tree], pos: SourcePosition, - indexed: IndexedContext + indexed: IndexedContext, + uri: URI, ): DefinitionResult = import indexed.ctx definitionsForSymbol( MetalsInteractive.enclosingSymbols(path, pos, indexed), + uri, pos ) end findDefinitions @@ -95,7 +99,8 @@ class PcDefinitionProvider( private def findTypeDefinitions( path: List[Tree], pos: SourcePosition, - indexed: IndexedContext + indexed: IndexedContext, + uri: URI, ): DefinitionResult = import indexed.ctx val enclosing = path.expandRangeToEnclosingApply(pos) @@ -108,24 +113,25 @@ class PcDefinitionProvider( case Nil => path.headOption match case Some(value: Literal) => - definitionsForSymbol(List(value.typeOpt.widen.typeSymbol), pos) + definitionsForSymbol(List(value.typeOpt.widen.typeSymbol), uri, pos) case _ => DefinitionResultImpl.empty case _ => - definitionsForSymbol(typeSymbols, pos) + definitionsForSymbol(typeSymbols, uri, pos) end findTypeDefinitions private def definitionsForSymbol( symbols: List[Symbol], + uri: URI, pos: SourcePosition )(using ctx: Context): DefinitionResult = symbols match case symbols @ (sym :: other) => val isLocal = sym.source == pos.source if isLocal then + val include = Include.definitions | Include.local val (exportedDefs, otherDefs) = - Interactive.findDefinitions(List(sym), driver, false, false) - .filter(_.source == sym.source) + Interactive.findTreesMatching(driver.openedTrees(uri), include, sym) .partition(_.tree.symbol.is(Exported)) otherDefs.headOption.orElse(exportedDefs.headOption) match diff --git a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala index 4b1b3f5fe7ba..cf4929dfc91d 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/PcInlayHintsProvider.scala @@ -57,7 +57,7 @@ class PcInlayHintsProvider( .headOption .getOrElse(unit.tpdTree) .enclosedChildren(pos.span) - .flatMap(tpdTree => deepFolder(InlayHints.empty, tpdTree).result()) + .flatMap(tpdTree => deepFolder(InlayHints.empty(params.uri()), tpdTree).result()) private def adjustPos(pos: SourcePosition): SourcePosition = pos.adjust(text)._1 diff --git a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala index a44c16ecc748..dc53525480c3 100644 --- a/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala +++ b/presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala @@ -16,6 +16,7 @@ import scala.language.unsafeNulls import scala.meta.internal.metals.CompilerVirtualFileParams import scala.meta.internal.metals.EmptyCancelToken import scala.meta.internal.metals.EmptyReportContext +import scala.meta.internal.metals.PcQueryContext import scala.meta.internal.metals.ReportContext import scala.meta.internal.metals.ReportLevel import scala.meta.internal.metals.StdReportContext @@ -143,18 +144,18 @@ case class ScalaPresentationCompiler( override def semanticTokens( params: VirtualFileParams ): CompletableFuture[ju.List[Node]] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( new ju.ArrayList[Node](), params.token() ) { access => val driver = access.compiler() new PcSemanticTokensProvider(driver, params).provide().asJava - } + }(params.toQueryContext) override def inlayHints( params: InlayHintsParams ): ju.concurrent.CompletableFuture[ju.List[l.InlayHint]] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( new ju.ArrayList[l.InlayHint](), params.token(), ) { access => @@ -162,7 +163,7 @@ case class ScalaPresentationCompiler( new PcInlayHintsProvider(driver, params, search) .provide() .asJava - } + }(params.toQueryContext) override def getTasty( targetUri: URI, @@ -173,7 +174,7 @@ case class ScalaPresentationCompiler( } def complete(params: OffsetParams): CompletableFuture[l.CompletionList] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( EmptyCompletionList(), params.token() ) { access => @@ -188,44 +189,43 @@ case class ScalaPresentationCompiler( folderPath, completionItemPriority ).completions() - - } + }(params.toQueryContext) def definition(params: OffsetParams): CompletableFuture[DefinitionResult] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( DefinitionResultImpl.empty, params.token() ) { access => val driver = access.compiler() PcDefinitionProvider(driver, params, search).definitions() - } + }(params.toQueryContext) override def typeDefinition( params: OffsetParams ): CompletableFuture[DefinitionResult] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( DefinitionResultImpl.empty, params.token() ) { access => val driver = access.compiler() PcDefinitionProvider(driver, params, search).typeDefinitions() - } + }(params.toQueryContext) def documentHighlight( params: OffsetParams ): CompletableFuture[ju.List[DocumentHighlight]] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( List.empty[DocumentHighlight].asJava, params.token() ) { access => val driver = access.compiler() PcDocumentHighlightProvider(driver, params).highlights.asJava - } + }(params.toQueryContext) override def references( params: ReferencesRequest ): CompletableFuture[ju.List[ReferencesResult]] = - compilerAccess.withNonInterruptableCompiler(Some(params.file()))( + compilerAccess.withNonInterruptableCompiler( List.empty[ReferencesResult].asJava, params.file().token, ) { access => @@ -233,16 +233,16 @@ case class ScalaPresentationCompiler( PcReferencesProvider(driver, params) .references() .asJava - } + }(params.file().toQueryContext) def inferExpectedType(params: OffsetParams): CompletableFuture[ju.Optional[String]] = - compilerAccess.withInterruptableCompiler(Some(params))( + compilerAccess.withInterruptableCompiler( Optional.empty(), params.token, ) { access => val driver = access.compiler() new InferExpectedType(search, driver, params).infer().asJava - } + }(params.toQueryContext) def shutdown(): Unit = compilerAccess.shutdown() @@ -257,8 +257,6 @@ case class ScalaPresentationCompiler( symbol: String ): CompletableFuture[Optional[IPcSymbolInformation]] = compilerAccess.withNonInterruptableCompiler[Optional[IPcSymbolInformation]]( - None - )( Optional.empty(), EmptyCancelToken, ) { access => @@ -266,27 +264,27 @@ case class ScalaPresentationCompiler( .info(symbol) .map(_.asJava) .asJava - } + }(emptyQueryContext) def semanticdbTextDocument( filename: URI, code: String ): CompletableFuture[Array[Byte]] = val virtualFile = CompilerVirtualFileParams(filename, code) - compilerAccess.withNonInterruptableCompiler(Some(virtualFile))( + compilerAccess.withNonInterruptableCompiler( Array.empty[Byte], EmptyCancelToken ) { access => val driver = access.compiler() val provider = SemanticdbTextDocumentProvider(driver, folderPath) provider.textDocument(filename, code) - } + }(virtualFile.toQueryContext) def completionItemResolve( item: l.CompletionItem, symbol: String ): CompletableFuture[l.CompletionItem] = - compilerAccess.withNonInterruptableCompiler(None)( + compilerAccess.withNonInterruptableCompiler( item, EmptyCancelToken ) { access => @@ -294,7 +292,7 @@ case class ScalaPresentationCompiler( CompletionItemResolver.resolve(item, symbol, search, config)(using driver.currentCtx ) - } + }(emptyQueryContext) def autoImports( name: String, @@ -303,7 +301,7 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ ju.List[scala.meta.pc.AutoImportsResult] ] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( List.empty[scala.meta.pc.AutoImportsResult].asJava, params.token() ) { access => @@ -318,13 +316,13 @@ case class ScalaPresentationCompiler( ) .autoImports(isExtension) .asJava - } + }(params.toQueryContext) def implementAbstractMembers( params: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( empty, params.token() ) { pc => @@ -335,31 +333,31 @@ case class ScalaPresentationCompiler( search, config ) - } + }(params.toQueryContext) end implementAbstractMembers override def insertInferredType( params: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( empty, params.token() ) { pc => new InferredTypeProvider(params, pc.compiler(), config, search) .inferredTypeEdits() .asJava - } + }(params.toQueryContext) override def inlineValue( params: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: Either[String, List[l.TextEdit]] = Right(List()) (compilerAccess - .withInterruptableCompiler(Some(params))(empty, params.token()) { pc => + .withInterruptableCompiler(empty, params.token()) { pc => new PcInlineValueProviderImpl(pc.compiler(), params) .getInlineTextEdits() - }) + }(params.toQueryContext)) .thenApply { case Right(edits: List[TextEdit]) => edits.asJava case Left(error: String) => throw new DisplayableException(error) @@ -371,7 +369,7 @@ case class ScalaPresentationCompiler( extractionPos: OffsetParams ): CompletableFuture[ju.List[l.TextEdit]] = val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]() - compilerAccess.withInterruptableCompiler(Some(range))(empty, range.token()) { + compilerAccess.withInterruptableCompiler(empty, range.token()) { pc => new ExtractMethodProvider( range, @@ -382,7 +380,7 @@ case class ScalaPresentationCompiler( ) .extractMethod() .asJava - } + }(range.toQueryContext) end extractMethod override def convertToNamedArguments( @@ -397,13 +395,13 @@ case class ScalaPresentationCompiler( ): CompletableFuture[ju.List[l.TextEdit]] = val empty: Either[String, List[l.TextEdit]] = Right(List()) (compilerAccess - .withNonInterruptableCompiler(Some(params))(empty, params.token()) { pc => + .withNonInterruptableCompiler(empty, params.token()) { pc => new ConvertToNamedArgumentsProvider( pc.compiler(), params, argIndices ).convertToNamedArguments - }) + }(params.toQueryContext)) .thenApplyAsync { case Left(error: String) => throw new DisplayableException(error) case Right(edits: List[l.TextEdit]) => edits.asJava @@ -413,33 +411,33 @@ case class ScalaPresentationCompiler( params: ju.List[OffsetParams] ): CompletableFuture[ju.List[l.SelectionRange]] = CompletableFuture.completedFuture { - compilerAccess.withSharedCompiler(params.asScala.headOption)( + compilerAccess.withSharedCompiler( List.empty[l.SelectionRange].asJava ) { pc => new SelectionRangeProvider( pc.compiler(), params, ).selectionRange().asJava - } + }(params.asScala.headOption.map(_.toQueryContext).getOrElse(emptyQueryContext)) } end selectionRange def hover( params: OffsetParams ): CompletableFuture[ju.Optional[HoverSignature]] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( ju.Optional.empty[HoverSignature](), params.token() ) { access => val driver = access.compiler() HoverProvider.hover(params, driver, search, config.hoverContentType()) - } + }(params.toQueryContext) end hover def prepareRename( params: OffsetParams ): CompletableFuture[ju.Optional[l.Range]] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( Optional.empty[l.Range](), params.token() ) { access => @@ -447,19 +445,19 @@ case class ScalaPresentationCompiler( Optional.ofNullable( PcRenameProvider(driver, params, None).prepareRename().orNull ) - } + }(params.toQueryContext) def rename( params: OffsetParams, name: String ): CompletableFuture[ju.List[l.TextEdit]] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( List[l.TextEdit]().asJava, params.token() ) { access => val driver = access.compiler() PcRenameProvider(driver, params, Some(name)).rename().asJava - } + }(params.toQueryContext) def newInstance( buildTargetIdentifier: String, @@ -473,13 +471,13 @@ case class ScalaPresentationCompiler( ) def signatureHelp(params: OffsetParams): CompletableFuture[l.SignatureHelp] = - compilerAccess.withNonInterruptableCompiler(Some(params))( + compilerAccess.withNonInterruptableCompiler( new l.SignatureHelp(), params.token() ) { access => val driver = access.compiler() SignatureHelpProvider.signatureHelp(driver, params, search) - } + }(params.toQueryContext) override def didChange( params: VirtualFileParams @@ -487,10 +485,10 @@ case class ScalaPresentationCompiler( CompletableFuture.completedFuture(Nil.asJava) override def didClose(uri: URI): Unit = - compilerAccess.withNonInterruptableCompiler(None)( + compilerAccess.withNonInterruptableCompiler( (), EmptyCancelToken - ) { access => access.compiler().close(uri) } + ) { access => access.compiler().close(uri) }(emptyQueryContext) override def withExecutorService( executorService: ExecutorService @@ -515,4 +513,19 @@ case class ScalaPresentationCompiler( override def isLoaded() = compilerAccess.isLoaded() + def additionalReportData() = + s"""|Scala version: $scalaVersion + |Classpath: + |${classpath + .map(path => s"$path [${if path.exists then "exists" else "missing"} ]") + .mkString(", ")} + |Options: + |${options.mkString(" ")} + |""".stripMargin + + extension (params: VirtualFileParams) + def toQueryContext = PcQueryContext(Some(params), additionalReportData) + + def emptyQueryContext = PcQueryContext(None, additionalReportData) + end ScalaPresentationCompiler diff --git a/presentation-compiler/test/dotty/tools/pc/tests/CompilerCachingSuite.scala b/presentation-compiler/test/dotty/tools/pc/tests/CompilerCachingSuite.scala index 5e13c07b9e5f..b2d837e2ff50 100644 --- a/presentation-compiler/test/dotty/tools/pc/tests/CompilerCachingSuite.scala +++ b/presentation-compiler/test/dotty/tools/pc/tests/CompilerCachingSuite.scala @@ -6,8 +6,10 @@ 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.internal.metals.EmptyCancelToken +import scala.meta.internal.metals.EmptyReportContext +import scala.meta.internal.metals.PcQueryContext import scala.meta.pc.OffsetParams import scala.concurrent.Future import scala.concurrent.Await @@ -26,20 +28,22 @@ class CompilerCachingSuite extends BasePCSuite: private def checkCompilationCount(expected: Int): Unit = presentationCompiler match case pc: ScalaPresentationCompiler => - val compilations = pc.compilerAccess.withNonInterruptableCompiler(None)(-1, EmptyCancelToken) { driver => + val compilations = pc.compilerAccess.withNonInterruptableCompiler(-1, EmptyCancelToken) { driver => driver.compiler().currentCtx.runId - }.get(timeout.length, timeout.unit) + }(emptyQueryContext).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(): Context = presentationCompiler match case pc: ScalaPresentationCompiler => - pc.compilerAccess.withNonInterruptableCompiler(None)(null, EmptyCancelToken) { driver => + pc.compilerAccess.withNonInterruptableCompiler(null, EmptyCancelToken) { driver => driver.compiler().currentCtx - }.get(timeout.length, timeout.unit) + }(emptyQueryContext).get(timeout.length, timeout.unit) case _ => throw IllegalStateException("Presentation compiler should always be of type of ScalaPresentationCompiler") + private def emptyQueryContext = PcQueryContext(None, () => "")(using EmptyReportContext) + @Before def beforeEach: Unit = presentationCompiler.restart() diff --git a/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala b/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala index b9d3fd411dcc..5fb38ad88e19 100644 --- a/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala +++ b/presentation-compiler/test/dotty/tools/pc/utils/TestInlayHints.scala @@ -3,8 +3,10 @@ package dotty.tools.pc.utils import scala.collection.mutable.ListBuffer import scala.meta.internal.jdk.CollectionConverters._ +import scala.meta.internal.pc.InlayHints import dotty.tools.pc.utils.InteractiveEnrichments.* +import com.google.gson.JsonElement import org.eclipse.lsp4j.InlayHint import org.eclipse.lsp4j.TextEdit import org.eclipse.{lsp4j => l} @@ -31,7 +33,7 @@ object TestInlayHints { case Right(labelParts) => labelParts.asScala.map(_.getValue()).toList } val data = - inlayHint.getData().asInstanceOf[Array[Any]] + InlayHints.fromData(inlayHint.getData().asInstanceOf[JsonElement])._2 buffer += "/*" labels.zip(data).foreach { case (label, data) => buffer += label.nn @@ -41,15 +43,13 @@ object TestInlayHints { buffer.toList.mkString } - private def readData(data: Any): List[String] = { - data match { - case data: String if data.isEmpty => Nil - case data: String => List("<<", data, ">>") - case data: l.Position => + private def readData(data: Either[String, l.Position]): List[String] = + data match + case Left("") => Nil + case Left(data) => List("<<", data, ">>") + case Right(data) => val str = s"(${data.getLine()}:${data.getCharacter()})" List("<<", str, ">>") - } - } def applyInlayHints(text: String, inlayHints: List[InlayHint]): String = { val textEdits = inlayHints.map { hint => diff --git a/project/Build.scala b/project/Build.scala index 29096670c747..463abab3f6fd 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1485,7 +1485,7 @@ object Build { BuildInfoPlugin.buildInfoDefaultSettings lazy val presentationCompilerSettings = { - val mtagsVersion = "1.4.2" + val mtagsVersion = "1.5.1" Seq( libraryDependencies ++= Seq( "org.lz4" % "lz4-java" % "1.8.0",