diff --git a/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala b/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala index 637ec186ab1..9b2e43a5968 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/Compilers.scala @@ -47,7 +47,6 @@ import org.eclipse.lsp4j.CompletionParams import org.eclipse.lsp4j.Diagnostic import org.eclipse.lsp4j.DocumentHighlight import org.eclipse.lsp4j.InitializeParams -import org.eclipse.lsp4j.Location import org.eclipse.lsp4j.ReferenceParams import org.eclipse.lsp4j.RenameParams import org.eclipse.lsp4j.SelectionRange @@ -690,7 +689,7 @@ class Compilers( params: ReferenceParams, targetFiles: List[AbsolutePath], token: CancelToken, - ): Future[List[Location]] = { + ): Future[List[ReferencesResult]] = { withPCAndAdjustLsp(params) { (pc, pos, adjust) => val targets = targetFiles.map { target => target.toURI.toString -> { @@ -712,9 +711,14 @@ class Compilers( params.getContext().isIncludeDeclaration(), ).asScala .map( - _.asScala.toList.map(loc => - targets(loc.getUri())._2.adjustLocation(loc) - ) + _.asScala.toList.map { defRes => + val locations = defRes + .locations() + .asScala + .toList + .map(loc => targets(loc.getUri())._2.adjustLocation(loc)) + ReferencesResult(defRes.symbol(), locations) + } ) } }.getOrElse(Future.successful(Nil)) diff --git a/metals/src/main/scala/scala/meta/internal/metals/Indexer.scala b/metals/src/main/scala/scala/meta/internal/metals/Indexer.scala index 57ddb7b2006..c271e461056 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/Indexer.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/Indexer.scala @@ -541,6 +541,7 @@ final case class Indexer( val input = sourceToIndex0.toInput val symbols = ArrayBuffer.empty[WorkspaceSymbolInformation] val methodSymbols = ArrayBuffer.empty[WorkspaceSymbolInformation] + referencesProvider().indexTokens(source, input, dialect) SemanticdbDefinition.foreach(input, dialect, includeMembers = true) { case SemanticdbDefinition(info, occ, owner) => if (info.isExtension) { diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala index 436057ab69d..7c14afdb80c 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsLspService.scala @@ -74,7 +74,6 @@ import scala.meta.internal.parsing.ClassFinder import scala.meta.internal.parsing.ClassFinderGranularity import scala.meta.internal.parsing.DocumentSymbolProvider import scala.meta.internal.parsing.FoldingRangeProvider -import scala.meta.internal.parsing.TokenEditDistance import scala.meta.internal.parsing.Trees import scala.meta.internal.remotels.RemoteLanguageServer import scala.meta.internal.rename.RenameProvider @@ -607,10 +606,10 @@ class MetalsLspService( semanticdbs, buffers, definitionProvider, - remote, trees, buildTargets, compilers, + scalaVersionSelector, ) private val syntheticHoverProvider: SyntheticHoverProvider = @@ -1209,10 +1208,12 @@ class MetalsLspService( val path = params.getTextDocument.getUri.toAbsolutePath savedFiles.add(path) // read file from disk, we only remove files from buffers on didClose. - buffers.put(path, path.toInput.text) + val text = path.toInput.text + buffers.put(path, text) Future .sequence( List( + referencesProvider.indexTokens(path, text), renameProvider.runSave(), parseTrees(path), onChange(List(path)), @@ -1524,65 +1525,6 @@ class MetalsLspService( referencesResult(params).map(_.flatMap(_.locations).asJava) } - // Triggers a cascade compilation and tries to find new references to a given symbol. - // It's not possible to stream reference results so if we find new symbols we notify the - // user to run references again to see updated results. - private def compileAndLookForNewReferences( - params: ReferenceParams, - result: List[ReferencesResult], - ): Future[Unit] = { - val path = params.getTextDocument.getUri.toAbsolutePath - val old = path.toInputFromBuffers(buffers) - compilations.cascadeCompileFiles(Seq(path)).flatMap { _ => - val newBuffer = path.toInputFromBuffers(buffers) - val newParams: Option[ReferenceParams] = - if (newBuffer.text == old.text) Some(params) - else { - val edit = TokenEditDistance(old, newBuffer, trees) - edit - .getOrElse(TokenEditDistance.NoMatch) - .toRevised( - params.getPosition.getLine, - params.getPosition.getCharacter, - ) - .foldResult( - pos => { - params.getPosition.setLine(pos.startLine) - params.getPosition.setCharacter(pos.startColumn) - Some(params) - }, - () => Some(params), - () => None, - ) - } - newParams match { - case None => Future.unit - case Some(p) => - for { - newResult <- referencesProvider.references(p) - } yield { - val diff = newResult - .flatMap(_.locations) - .length - result.flatMap(_.locations).length - val diffSyms: Set[String] = - newResult.map(_.symbol).toSet -- result.map(_.symbol).toSet - if (diffSyms.nonEmpty && diff > 0) { - import scala.meta.internal.semanticdb.Scala._ - val names = - diffSyms - .map(sym => s"'${sym.desc.name.value}'") - .mkString(" and ") - val message = - s"Found new symbol references for $names, try running again." - scribe.info(message) - statusBar - .addMessage(clientConfig.icons.info + message) - } - } - } - } - } - def referencesResult( params: ReferenceParams ): Future[List[ReferencesResult]] = { @@ -1600,7 +1542,8 @@ class MetalsLspService( } } if (results.nonEmpty) { - compileAndLookForNewReferences(params, results) + val path = params.getTextDocument.getUri.toAbsolutePath + compilations.cascadeCompileFiles(Seq(path)) } results } diff --git a/metals/src/main/scala/scala/meta/internal/metals/ReferenceProvider.scala b/metals/src/main/scala/scala/meta/internal/metals/ReferenceProvider.scala index b71566b5b78..c4618f0c915 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/ReferenceProvider.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/ReferenceProvider.scala @@ -9,7 +9,9 @@ import scala.concurrent.Future import scala.util.control.NonFatal import scala.util.matching.Regex +import scala.meta.Dialect import scala.meta.Importee +import scala.meta.inputs.Input import scala.meta.internal.metals.MetalsEnrichments._ import scala.meta.internal.metals.ResolvedSymbolOccurrence import scala.meta.internal.mtags.DefinitionAlternatives.GlobalSymbol @@ -17,20 +19,23 @@ import scala.meta.internal.mtags.Semanticdbs import scala.meta.internal.mtags.Symbol import scala.meta.internal.parsing.TokenEditDistance import scala.meta.internal.parsing.Trees -import scala.meta.internal.remotels.RemoteLanguageServer import scala.meta.internal.semanticdb.Scala._ import scala.meta.internal.semanticdb.SymbolInformation import scala.meta.internal.semanticdb.SymbolOccurrence import scala.meta.internal.semanticdb.Synthetic import scala.meta.internal.semanticdb.TextDocument import scala.meta.internal.semanticdb.TextDocuments +import scala.meta.internal.tokenizers.LegacyScanner +import scala.meta.internal.tokenizers.LegacyToken._ import scala.meta.internal.{semanticdb => s} import scala.meta.io.AbsolutePath +import scala.meta.tokens.Token.Ident import ch.epfl.scala.bsp4j.BuildTargetIdentifier import com.google.common.hash.BloomFilter import com.google.common.hash.Funnels import org.eclipse.lsp4j.Location +import org.eclipse.lsp4j.Position import org.eclipse.lsp4j.ReferenceParams final class ReferenceProvider( @@ -38,10 +43,10 @@ final class ReferenceProvider( semanticdbs: Semanticdbs, buffers: Buffers, definition: DefinitionProvider, - remote: RemoteLanguageServer, trees: Trees, buildTargets: BuildTargets, compilers: Compilers, + scalaVersionSelector: ScalaVersionSelector, )(implicit ec: ExecutionContext) extends SemanticdbFeatureProvider { @@ -50,6 +55,7 @@ final class ReferenceProvider( bloom: BloomFilter[CharSequence], ) val index: TrieMap[Path, IndexEntry] = TrieMap.empty + val tokenIndex: TrieMap[Path, IndexEntry] = TrieMap.empty override def reset(): Unit = { index.clear() @@ -59,6 +65,40 @@ final class ReferenceProvider( index.remove(file.toNIO) } + def indexTokens( + path: AbsolutePath, + text: String, + ): Future[Unit] = Future { + val dialect = scalaVersionSelector.getDialect(path) + indexTokens(path, Input.String(text), dialect) + } + + def indexTokens( + file: AbsolutePath, + input: Input, + dialect: Dialect, + ): Unit = + buildTargets.inverseSources(file).map { id => + var count = 0 + new LegacyScanner(input, dialect).foreach { + case ident if ident.token == IDENTIFIER => count += 1 + case _ => + } + + val bloom = BloomFilter.create( + Funnels.stringFunnel(StandardCharsets.UTF_8), + Integer.valueOf(count * 2), + 0.01, + ) + + val entry = IndexEntry(id, bloom) + tokenIndex(file.toNIO) = entry + new LegacyScanner(input, dialect).foreach { + case ident if ident.token == IDENTIFIER => bloom.put(ident.name) + case _ => + } + } + override def onChange(docs: TextDocuments, file: AbsolutePath): Unit = { buildTargets.inverseSources(file).map { id => val count = docs.documents.foldLeft(0)(_ + _.occurrences.length) @@ -146,7 +186,7 @@ final class ReferenceProvider( s"No symbol found at ${params.getPosition()} for $source" ) } - Future.sequence { + val semanticdbResult = Future.sequence { results.map { result => val occurrence = result.occurrence.get val distance = result.distance @@ -166,18 +206,22 @@ final class ReferenceProvider( locations.map(ReferencesResult(occurrence.symbol, _)) } } + val pcResult = + pcReferences(source, params, path => !index.contains(path.toNIO)) + + Future + .sequence(List(semanticdbResult, pcResult)) + .map( + _.flatten + .groupBy(_.symbol) + .map { case (symbol, refs) => + ReferencesResult(symbol, refs.flatMap(_.locations)) + } + .toList + ) case None => scribe.debug(s"No semanticdb for $source") - // NOTE(olafur): we block here instead of returning a Future because it - // requires a significant refactoring to make the reference provider and - // its dependencies (including rename provider) asynchronous. The remote - // language server returns `Future.successful(None)` when it's disabled - // so this isn't even blocking for normal usage of Metals. - Future.successful( - List( - remote.referencesBlocking(params).getOrElse(ReferencesResult.empty) - ) - ) + pcReferences(source, params) } } @@ -269,6 +313,57 @@ final class ReferenceProvider( } } + private def pcReferences( + path: AbsolutePath, + params: ReferenceParams, + filterTargetFiles: AbsolutePath => Boolean = _ => true, + ): Future[List[ReferencesResult]] = { + val result = for { + name <- nameAtPosition(path, params.getPosition()) + buildTarget <- buildTargets.inverseSources(path) + targetFiles = (path :: pathsForName(buildTarget, name).toList).distinct + .filter(filterTargetFiles) + if targetFiles.nonEmpty + } yield compilers + .references(params, targetFiles, EmptyCancelToken) + result.getOrElse(Future.successful(Nil)) + } + + private def nameAtPosition( + path: AbsolutePath, + position: Position, + ) = + for { + text <- buffers.get(path) + input = Input.String(text) + pos <- position.toMeta(input) + tree <- trees.get(path) + token <- tree.tokens.find { t => t.pos.encloses(pos) } + ident <- token match { + case _: Ident => Some(token) + case _ => None + } + } yield ident.text + + private def pathsForName( + buildTarget: BuildTargetIdentifier, + name: String, + ): Iterator[AbsolutePath] = { + val allowedBuildTargets = buildTargets.allInverseDependencies(buildTarget) + val visited = scala.collection.mutable.Set.empty[AbsolutePath] + val result = for { + (path, entry) <- tokenIndex.iterator + if allowedBuildTargets.contains(entry.id) && + entry.bloom.mightContain(name) + sourcePath = AbsolutePath(path) + if !visited(sourcePath) + _ = visited.add(sourcePath) + if sourcePath.exists + } yield sourcePath + + result + } + /** * Return all paths to files which contain at least one symbol from isSymbol set. */ @@ -366,20 +461,22 @@ final class ReferenceProvider( ): Future[Seq[Location]] = { val isSymbol = alternatives + occ.symbol val isLocal = occ.symbol.isLocal + if (isLocal) + compilers + .references(params, List(source), EmptyCancelToken) + .map(_.flatMap(_.locations)) + else { + /* search local in the following cases: + * - it's a dependency source. + * We can't search references inside dependencies so at least show them in a source file. + * - it's a standalone file that doesn't belong to any build target + */ + val searchLocal = + source.isDependencySource(workspace) || + buildTargets.inverseSources(source).isEmpty - /* search local in the following cases: - * - it's a dependency source. - * We can't search references inside dependencies so at least show them in a source file. - * - it's a standalone file that doesn't belong to any build target - */ - val searchLocal = - isLocal || source.isDependencySource(workspace) || - buildTargets.inverseSources(source).isEmpty - val local = - if (isLocal && !source.isJava) - compilers.references(params, List(source), EmptyCancelToken) - else if (searchLocal) - Future.successful( + val local = + if (searchLocal) referenceLocations( snapshot, isSymbol, @@ -390,11 +487,9 @@ final class ReferenceProvider( includeSynthetics, source.isJava, ) - ) - else Future.successful(Seq.empty) + else Seq.empty - val workspaceRefs = - if (!isLocal) + val workspaceRefs = workspaceReferences( source, isSymbol, @@ -402,10 +497,8 @@ final class ReferenceProvider( findRealRange, includeSynthetics, ) - else - Seq.empty - - local.map(workspaceRefs ++ _) + Future.successful(local ++ workspaceRefs) + } } private def referenceLocations( diff --git a/mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompiler.java b/mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompiler.java index 7a927eab34f..21baac877b9 100644 --- a/mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompiler.java +++ b/mtags-interfaces/src/main/java/scala/meta/pc/PresentationCompiler.java @@ -114,7 +114,7 @@ public CompletableFuture> semanticTokens(VirtualFileParams params) { /** * Returns the references of the symbol under the current position in the target files. */ - public CompletableFuture> references(OffsetParams params, java.util.List targetFiles, boolean includeDefinition) { + public CompletableFuture> references(OffsetParams params, java.util.List targetFiles, boolean includeDefinition) { return CompletableFuture.completedFuture(Collections.emptyList()); } diff --git a/mtags-java/src/main/scala/scala/meta/internal/pc/JavaPresentationCompiler.scala b/mtags-java/src/main/scala/scala/meta/internal/pc/JavaPresentationCompiler.scala index f94ef52d0d7..71063cec09e 100644 --- a/mtags-java/src/main/scala/scala/meta/internal/pc/JavaPresentationCompiler.scala +++ b/mtags-java/src/main/scala/scala/meta/internal/pc/JavaPresentationCompiler.scala @@ -103,7 +103,7 @@ case class JavaPresentationCompiler( params: OffsetParams, targetFiles: util.List[VirtualFileParams], includeDefinition: Boolean - ): CompletableFuture[util.List[lsp4j.Location]] = + ): CompletableFuture[util.List[DefinitionResult]] = CompletableFuture.completedFuture(Nil.asJava) override def getTasty( diff --git a/mtags/src/main/scala-2/scala/meta/internal/pc/PcReferencesProvider.scala b/mtags/src/main/scala-2/scala/meta/internal/pc/PcReferencesProvider.scala index f4b55502f29..98b591246a7 100644 --- a/mtags/src/main/scala-2/scala/meta/internal/pc/PcReferencesProvider.scala +++ b/mtags/src/main/scala-2/scala/meta/internal/pc/PcReferencesProvider.scala @@ -1,6 +1,8 @@ package scala.meta.internal.pc +import scala.meta.internal.jdk.CollectionConverters._ import scala.meta.internal.mtags.MtagsEnrichments._ +import scala.meta.pc.DefinitionResult import scala.meta.pc.OffsetParams import scala.meta.pc.VirtualFileParams @@ -13,23 +15,26 @@ class PcReferencesProvider( includeDefinition: Boolean ) extends WithCompilationUnit(compiler, params) with PcSymbolSearch { - def result(): List[l.Location] = - for { + def result(): List[DefinitionResult] = { + val result = for { (sought, _) <- soughtSymbols.toList - params <- targetFiles + params <- + if (sought.forall(_.isLocalToBlock)) + targetFiles.find(_.uri() == params.uri()).toList + else targetFiles collected <- { val collector = new WithCompilationUnit(compiler, params) - with PcCollector[Option[l.Range]] { + with PcCollector[Option[(String, l.Range)]] { import compiler._ override def collect(parent: Option[Tree])( tree: Tree, toAdjust: Position, sym: Option[compiler.Symbol] - ): Option[l.Range] = { + ): Option[(String, l.Range)] = { val (pos, _) = toAdjust.adjust(text) tree match { case _: DefTree if !includeDefinition => None - case _ => Some(pos.toLsp) + case t => Some(compiler.semanticdbSymbol(t.symbol), pos.toLsp) } } } @@ -37,7 +42,16 @@ class PcReferencesProvider( collector .resultWithSought(sought.asInstanceOf[Set[collector.compiler.Symbol]]) .flatten - .map(new l.Location(params.uri().toString(), _)) + .map { case (symbol, range) => + (symbol, new l.Location(params.uri().toString(), range)) + } } } yield collected + result + .groupBy(_._1) + .map { case (symbol, locs) => + DefinitionResultImpl(symbol, locs.map(_._2).asJava) + } + .toList + } } diff --git a/mtags/src/main/scala-2/scala/meta/internal/pc/ScalaPresentationCompiler.scala b/mtags/src/main/scala-2/scala/meta/internal/pc/ScalaPresentationCompiler.scala index ef7e1f47933..f201a1a10bd 100644 --- a/mtags/src/main/scala-2/scala/meta/internal/pc/ScalaPresentationCompiler.scala +++ b/mtags/src/main/scala-2/scala/meta/internal/pc/ScalaPresentationCompiler.scala @@ -45,7 +45,6 @@ import org.eclipse.lsp4j.CompletionItem import org.eclipse.lsp4j.CompletionList import org.eclipse.lsp4j.Diagnostic import org.eclipse.lsp4j.DocumentHighlight -import org.eclipse.lsp4j.Location import org.eclipse.lsp4j.Range import org.eclipse.lsp4j.SelectionRange import org.eclipse.lsp4j.SignatureHelp @@ -363,9 +362,9 @@ case class ScalaPresentationCompiler( params: OffsetParams, targetFiles: ju.List[VirtualFileParams], includeDefinition: Boolean - ): CompletableFuture[ju.List[Location]] = + ): CompletableFuture[ju.List[DefinitionResult]] = compilerAccess.withInterruptableCompiler(Some(params))( - List.empty[Location].asJava, + List.empty[DefinitionResult].asJava, params.token() ) { pc => new PcReferencesProvider( diff --git a/mtags/src/main/scala-3-wrapper/ScalaPresentationCompiler.scala b/mtags/src/main/scala-3-wrapper/ScalaPresentationCompiler.scala index babddb017a9..b2b364617cd 100644 --- a/mtags/src/main/scala-3-wrapper/ScalaPresentationCompiler.scala +++ b/mtags/src/main/scala-3-wrapper/ScalaPresentationCompiler.scala @@ -182,25 +182,24 @@ case class ScalaPresentationCompiler( params: OffsetParams, targetFiles: ju.List[VirtualFileParams], includeDefinition: Boolean, - ): CompletableFuture[ju.List[Location]] = + ): CompletableFuture[ju.List[DefinitionResult]] = targetFiles.asScala.toList match case file :: Nil if file.uri() == params.uri() => FutureConverters .toJava( FutureConverters .toScala(documentHighlight(params)) - .map( - _.asScala - .collect { - case highlight - if highlight.getKind() == DocumentHighlightKind.Read || includeDefinition => - new Location( - params.uri().toString(), - highlight.getRange(), - ) - } - .asJava - ) + .map { hightLightResult => + val locations = hightLightResult.asScala.collect { + case highlight + if highlight.getKind() == DocumentHighlightKind.Read || includeDefinition => + new Location( + params.uri().toString(), + highlight.getRange(), + ) + }.asJava + List(new DefinitionResultImpl("", locations)).asJava + } ) .toCompletableFuture case _ => diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/PcReferencesProvider.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/PcReferencesProvider.scala index 01fca8e1d90..f52409795f0 100644 --- a/mtags/src/main/scala-3/scala/meta/internal/pc/PcReferencesProvider.scala +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/PcReferencesProvider.scala @@ -1,12 +1,17 @@ package scala.meta.internal.pc +import scala.collection.JavaConverters.* + import scala.meta.internal.mtags.MtagsEnrichments.* +import scala.meta.pc.DefinitionResult import scala.meta.pc.OffsetParams import scala.meta.pc.VirtualFileParams import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.ast.tpd.* -import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.Symbols.* import dotty.tools.dotc.interactive.InteractiveDriver import dotty.tools.dotc.util.SourcePosition import org.eclipse.lsp4j @@ -19,27 +24,45 @@ class PcReferencesProvider( includeDefinition: Boolean, ): val symbolSearch = new WithCompilationUnit(driver, params) with PcSymbolSearch - def result(): List[Location] = - for - (sought, _) <- symbolSearch.soughtSymbols.toList - params <- targetFiles - collected <- collectForFile(sought, params) - yield collected + def result(): List[DefinitionResult] = + val result = + for + (sought, _) <- symbolSearch.soughtSymbols.toList + params <- + given Context = symbolSearch.ctx + + if sought.forall(_.is(Flags.Local)) + then targetFiles.find(_.uri() == params.uri()).toList + else targetFiles + + collected <- collectForFile(sought, params) + yield collected + result + .groupBy(_._1) + .map { case (symbol, locs) => + DefinitionResultImpl(symbol, locs.map(_._2).asJava) + } + .toList + end result private def collectForFile(sought: Set[Symbol], params: VirtualFileParams) = new WithCompilationUnit(driver, params) - with PcCollector[Option[lsp4j.Range]]: + with PcCollector[Option[(String, lsp4j.Range)]]: def collect(parent: Option[Tree])( tree: Tree | EndMarker, toAdjust: SourcePosition, symbol: Option[Symbol], - ): Option[lsp4j.Range] = + ): Option[(String, lsp4j.Range)] = val (pos, _) = toAdjust.adjust(text) tree match case (_: DefTree) if !includeDefinition => None - case _: Tree => Some(pos.toLsp) + case t: Tree => + val sym = symbol.getOrElse(t.symbol) + Some(SemanticdbSymbols.symbolName(sym), pos.toLsp) case _ => None .resultWithSought(sought) .flatten - .map(new Location(params.uri().toString(), _)) + .map { case (symbol, range) => + (symbol, new Location(params.uri().toString(), range)) + } end PcReferencesProvider diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/PcSymbolSearch.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/PcSymbolSearch.scala index 48730c9d2d4..d054745986c 100644 --- a/mtags/src/main/scala-3/scala/meta/internal/pc/PcSymbolSearch.scala +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/PcSymbolSearch.scala @@ -343,4 +343,5 @@ object PcSymbolSearch: nameSpan.start, nameSpan.end, ) != df.name.toString() + end PcSymbolSearch diff --git a/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala b/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala index d3acbb2e742..8a5dce5fdac 100644 --- a/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala +++ b/mtags/src/main/scala-3/scala/meta/internal/pc/ScalaPresentationCompiler.scala @@ -172,9 +172,9 @@ case class ScalaPresentationCompiler( params: OffsetParams, targetFiles: ju.List[VirtualFileParams], includeDefinition: Boolean, - ): CompletableFuture[ju.List[l.Location]] = + ): CompletableFuture[ju.List[DefinitionResult]] = compilerAccess.withNonInterruptableCompiler(Some(params))( - List.empty[l.Location].asJava, + List.empty[DefinitionResult].asJava, params.token, ) { access => val driver = access.compiler() diff --git a/tests/slow/src/test/scala/tests/feature/PcReferencesLspSuite.scala b/tests/slow/src/test/scala/tests/feature/PcReferencesLspSuite.scala index ae6ca1efc57..6702c0a55b0 100644 --- a/tests/slow/src/test/scala/tests/feature/PcReferencesLspSuite.scala +++ b/tests/slow/src/test/scala/tests/feature/PcReferencesLspSuite.scala @@ -5,7 +5,6 @@ import scala.meta.internal.metals.EmptyCancelToken import scala.meta.internal.metals.MetalsEnrichments._ import munit.TestOptions -import org.eclipse.lsp4j.Location import org.eclipse.lsp4j.ReferenceContext import org.eclipse.lsp4j.ReferenceParams import tests.BaseLspSuite @@ -78,10 +77,12 @@ class PcReferencesLspSuite val layout = input.replaceAll("<<|>>|@@", "") - def renderObtained(refs: List[Location]): String = { - val refsMap = refs.groupMap(ref => - ref.getUri().toAbsolutePath.toRelative(workspace).toString - )(_.getRange()) + def renderObtained(refs: List[metals.ReferencesResult]): String = { + val refsMap = refs + .flatMap(_.locations) + .groupMap(ref => + ref.getUri().toAbsolutePath.toRelative(workspace).toString + )(_.getRange()) files .map { case (pathStr, content) => val actualContent = content.replaceAll("<<|>>", "") diff --git a/tests/slow/src/test/scala/tests/sbt/SbtServerSuite.scala b/tests/slow/src/test/scala/tests/sbt/SbtServerSuite.scala index 27c2e4b3e8a..2aedd8a82ad 100644 --- a/tests/slow/src/test/scala/tests/sbt/SbtServerSuite.scala +++ b/tests/slow/src/test/scala/tests/sbt/SbtServerSuite.scala @@ -526,4 +526,50 @@ class SbtServerSuite } yield () } + test("meta-build-references") { + cleanWorkspace() + + val buildSbt = + s"""|${SbtBuildLayout.commonSbtSettings} + |ThisBuild / scalaVersion := "${V.scala213}" + |val a = project.in(file(V.<>)) + |""".stripMargin + val buildSbtBase = buildSbt.replaceAll("<<|>>", "") + + val v = + s"""|object V { + | val <> = "a" + |} + |""".stripMargin + val vBase = v.replaceAll("<<|>>|@@", "") + + for { + _ <- initialize( + s"""|/project/build.properties + |sbt.version=${V.sbtVersion} + |/project/V.scala + |$vBase + |/build.sbt + |$buildSbtBase + |""".stripMargin + ) + _ <- server.server.indexingPromise.future + _ <- server.didOpen("project/V.scala") + _ <- + server.assertReferences( + "project/V.scala", + v.replaceAll("<<|>>", ""), + Map( + "project/V.scala" -> v.replaceAll("@@", ""), + "build.sbt" -> buildSbt, + ), + Map( + "project/V.scala" -> vBase, + "build.sbt" -> buildSbtBase, + ), + ) + + } yield () + } + }