diff --git a/mtags/src/main/scala/scala/meta/internal/mtags/JavaToplevelMtags.scala b/mtags/src/main/scala/scala/meta/internal/mtags/JavaToplevelMtags.scala index 1c1043af043..4c57145b317 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/JavaToplevelMtags.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/JavaToplevelMtags.scala @@ -27,6 +27,14 @@ class JavaToplevelMtags(val input: Input.VirtualFile) extends MtagsIndexer { } } + def readPackage: List[String] = { + fetchToken // start of file + fetchToken match { + case Token.Package => readPaths.map(_.value) + case _ => Nil + } + } + private def loop: Unit = { val token = fetchToken token match { diff --git a/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala b/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala index 244029810aa..6e3d5cee52a 100644 --- a/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala +++ b/mtags/src/main/scala/scala/meta/internal/mtags/SymbolIndexBucket.scala @@ -11,6 +11,7 @@ import scala.util.control.NonFatal import scala.meta.Dialect import scala.meta.internal.io.FileIO import scala.meta.internal.io.PathIO +import scala.meta.internal.mtags.JavaToplevelMtags import scala.meta.internal.mtags.ScalametaCommonEnrichments._ import scala.meta.internal.semanticdb.Scala._ import scala.meta.internal.{semanticdb => s} @@ -65,6 +66,8 @@ class SymbolIndexBucket( root.listRecursive.toList.flatMap { case source if source.isScala => addSourceFile(source, None).map(sym => (sym, source)) + case source if source.isJava => + addJavaSourceFile(source).map(sym => (sym, source)) case _ => List.empty } @@ -91,6 +94,33 @@ class SymbolIndexBucket( } } + /* Sometimes source jars have additional nested directories, + * in that case java toplevel is not "trivial". + * See: https://github.com/scalameta/metals/issues/3815 + */ + def addJavaSourceFile(source: AbsolutePath): List[String] = { + new JavaToplevelMtags(source.toInput).readPackage match { + case Nil => Nil + case packageParts => + val className = source.filename.stripSuffix(".java") + val symbol = packageParts.mkString("", "/", s"/$className#") + if ( + isTrivialToplevelSymbol( + source.toURI.toString, + symbol, + extension = "java" + ) + ) Nil + else { + toplevels.updateWith(symbol) { + case Some(acc) => Some(acc + source) + case None => Some(Set(source)) + } + List(symbol) + } + } + } + def addSourceFile( source: AbsolutePath, sourceDirectory: Option[AbsolutePath] @@ -121,9 +151,13 @@ class SymbolIndexBucket( // Returns true if symbol is com/foo/Bar# and path is /com/foo/Bar.scala // Such symbols are "trivial" because their definition location can be computed // on the fly. - private def isTrivialToplevelSymbol(path: String, symbol: String): Boolean = { + private def isTrivialToplevelSymbol( + path: String, + symbol: String, + extension: String = "scala" + ): Boolean = { val pathBuffer = - CharBuffer.wrap(path).subSequence(1, path.length - ".scala".length) + CharBuffer.wrap(path).subSequence(1, path.length - extension.length - 1) val symbolBuffer = CharBuffer.wrap(symbol).subSequence(0, symbol.length - 1) pathBuffer.equals(symbolBuffer) diff --git a/tests/unit/src/test/scala/tests/DefinitionLspSuite.scala b/tests/unit/src/test/scala/tests/DefinitionLspSuite.scala index b94044407b4..928dac4322e 100644 --- a/tests/unit/src/test/scala/tests/DefinitionLspSuite.scala +++ b/tests/unit/src/test/scala/tests/DefinitionLspSuite.scala @@ -714,4 +714,42 @@ class DefinitionLspSuite } yield () } + test("nested-jars") { + cleanWorkspace() + for { + _ <- initialize( + s""" + |/metals.json + |{ + | "a": { + | "libraryDependencies": [ + | "com.daml:bindings-rxjava:2.0.0" + | ] + | } + |} + |/a/src/main/scala/a/Main.scala + |package a + |import com.daml.ledger.rxjava.DamlLedgerClient + | + |object O { + | val k: DamlLedgerClient = ??? + |} + |""".stripMargin + ) + _ <- server.didOpen("a/src/main/scala/a/Main.scala") + _ = assertNoDiagnostics() + _ = assertNoDiff( + server.workspaceDefinitions, + """|/a/src/main/scala/a/Main.scala + |package a + |import com.daml.ledger.rxjava.DamlLedgerClient/*DamlLedgerClient.java*/ + | + |object O/*L3*/ { + | val k/*L4*/: DamlLedgerClient/*DamlLedgerClient.java*/ = ???/*Predef.scala*/ + |} + |""".stripMargin, + ) + } yield () + } + }