Skip to content

Commit

Permalink
Fix java semanticdb issues, Doctor and goto refs
Browse files Browse the repository at this point in the history
  • Loading branch information
Arthurm1 committed Jul 27, 2021
1 parent 4bd2bdb commit 1f8c0c1
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 48 deletions.
4 changes: 2 additions & 2 deletions metals/src/main/scala/scala/meta/internal/builds/Digest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,15 @@ object Digest {
val ext = PathIO.extension(path.toNIO)
val isScala = Set("sbt", "scala", "sc")(ext)
// we can have both gradle and gradle.kts and build plugins can be written in any of three languages
val isGradle =
val isGeneralJVM =
Set("gradle", "groovy", "gradle.kts", "java", "kts").exists(
path.toString().endsWith(_)
)
val isXml = ext == "xml"

if (isScala && path.isFile) {
digestScala(path, digest)
} else if (isGradle && path.isFile) {
} else if (isGeneralJVM && path.isFile) {
digestGeneralJvm(path, digest)
} else if (isXml) {
digestXml(path, digest)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package scala.meta.internal.metals
import java.io.IOException
import java.io.InputStream
import java.net.URI
import java.util.Collections
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
Expand Down Expand Up @@ -382,7 +381,7 @@ object BuildServerConnection {
BuildInfo.bspVersion,
workspace.toURI.toString,
new BuildClientCapabilities(
Collections.singletonList("scala")
List("scala", "java").asJava
)
)
val gson = new Gson
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@ final class BuildTargets(
)
if (isSupportedScalaVersion) score <<= 2

// TODO(@arthurm1) What score should java only build target get?
// possibly <<=1 as it's JVM but not yet supported by Metals?
// this will affect scalac targets that have javacoptions
val usesJavac = javacOptions(t).nonEmpty
if (usesJavac) score <<= 1

val isJVM = scalacOptions(t).exists(_.isJVM)
if (isJVM) score <<= 1

// note(@tgodzik) once the support for Scala 3 is on par with Scala 2 this can be removed
val isScala2 = scalaInfo(t).exists(info =>
!ScalaVersions.isScala3Version(info.getScalaVersion())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import java.{util => ju}
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.io.AbsolutePath

import ch.epfl.scala.bsp4j.BuildTargetIdentifier
import ch.epfl.scala.bsp4j.BuildTarget
import ch.epfl.scala.bsp4j.BuildTargetIdentifier

trait CommonTarget {

Expand Down
54 changes: 33 additions & 21 deletions metals/src/main/scala/scala/meta/internal/metals/Doctor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ final class Doctor(

} else {
val targetResults =
targets.sortBy(_.baseDirectory).map(extractTargetInfo)
targets.sortBy(_.baseDirectory).flatMap(extractTargetInfo)
DoctorResults(doctorTitle, heading, None, Some(targetResults)).toJson
}
ujson.write(results)
Expand Down Expand Up @@ -336,27 +336,33 @@ final class Doctor(
targets: List[CommonTarget]
): Unit = {
targets.sortBy(_.baseDirectory).foreach { target =>
val targetInfo = extractTargetInfo(target)
val center = "style='text-align: center'"
html.element("tr")(
_.element("td")(_.text(targetInfo.name))
.element("td")(_.text(targetInfo.scalaVersion))
.element("td", center)(_.text(Icons.unicode.check))
.element("td", center)(_.text(targetInfo.definitionStatus))
.element("td", center)(_.text(targetInfo.completionsStatus))
.element("td", center)(_.text(targetInfo.referencesStatus))
.element("td")(
_.raw(targetInfo.recommenedFix)
)
)
extractTargetInfo(target).foreach { targetInfo =>
val center = "style='text-align: center'"
html.element("tr")(
_.element("td")(_.text(targetInfo.name))
.element("td")(_.text(targetInfo.scalaVersion))
.element("td", center)(_.text(Icons.unicode.check))
.element("td", center)(_.text(targetInfo.definitionStatus))
.element("td", center)(_.text(targetInfo.completionsStatus))
.element("td", center)(_.text(targetInfo.referencesStatus))
.element("td")(
_.raw(targetInfo.recommenedFix)
)
)
}
}
}

private def extractTargetInfo(target: CommonTarget) = {
target match {
case scalaTarget: ScalaTarget => extractScalaTargetInfo(scalaTarget)
case javaTarget: JavaTarget => extractJavaTargetInfo(javaTarget)
val scalaDoctorInfo = target match {
case scalaTarget: ScalaTarget => List(extractScalaTargetInfo(scalaTarget))
case _ => List.empty
}
val javaDoctorInfo = target match {
case javaTarget: JavaTarget => List(extractJavaTargetInfo(javaTarget))
case _ => List.empty
}
scalaDoctorInfo ::: javaDoctorInfo
}

private def extractScalaTargetInfo(target: ScalaTarget) = {
Expand Down Expand Up @@ -388,10 +394,16 @@ final class Doctor(

private def extractJavaTargetInfo(target: JavaTarget) = {
val scalaVersion = "Java"
val definition: String = Icons.unicode.alert
val completions: String = definition
val references: String = Icons.unicode.alert
val recommendedFix = "Java not supported yet"
val definition: String = Icons.unicode.check
val completions: String = Icons.unicode.alert
val isSemanticdbNeeded = !target.isJavaSemanticdbEnabled
val references: String =
if (isSemanticdbNeeded) {
Icons.unicode.alert
} else {
Icons.unicode.check
}
val recommendedFix = problemResolver.recommendation(target)
DoctorTargetInfo(
target.displayName,
scalaVersion,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package scala.meta.internal.metals

import java.{util => ju}

import scala.meta.internal.metals.MetalsEnrichments._

import ch.epfl.scala.bsp4j.BuildTarget
import ch.epfl.scala.bsp4j.JavacOptionsItem
import ch.epfl.scala.bsp4j.ScalaBuildTarget
import ch.epfl.scala.bsp4j.ScalacOptionsItem
import ch.epfl.scala.bsp4j.JavacOptionsItem

case class JavaScalaTarget(
override val info: BuildTarget,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -646,32 +646,33 @@ object MetalsEnrichments
}
implicit class XtensionJavacOptions(item: b.JavacOptionsItem) {
def targetroot: AbsolutePath = {
semanticdbFlag("targetroot")
item.getOptions.asScala
.find(_.startsWith("-Xplugin:semanticdb"))
.map(arg => {
// TODO(arthurm1) improve args parsing
val targetrootPos = arg.indexOf("-targetroot:")
val sourcerootPos = arg.indexOf("-sourceroot:")
if (targetrootPos > sourcerootPos)
arg.substring(targetrootPos + 12).trim()
else
arg.substring(sourcerootPos + 12, targetrootPos - 1).trim()
})
.filter(_ != "javac-classes-directory")
.map(AbsolutePath(_))
.getOrElse(item.getClassDirectory.toAbsolutePath)
}

def isSemanticdbEnabled: Boolean = {
item.getOptions.asScala.exists { opt =>
opt.startsWith("-Xplugin:") && opt
.contains("semanticdb-scalac")
opt.startsWith("-Xplugin:semanticdb")
}
}

def isSourcerootDeclared: Boolean = {
item.getOptions.asScala.exists { option =>
option.startsWith("-P:semanticdb:sourceroot")
}
}

/**
* Returns the value of a -P:semanticdb:$option:$value compiler flag.
*/
def semanticdbFlag(name: String): Option[String] = {
val flag = s"-P:semanticdb:$name:"
item.getOptions.asScala
.find(_.startsWith(flag))
.map(_.stripPrefix(flag))
.find(_.startsWith("-Xplugin:semanticdb"))
.map(_.contains("-sourceroot"))
.getOrElse(false)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2567,7 +2567,7 @@ class MetalsLanguageServer(
definitionOnly: Boolean = false
): Future[DefinitionResult] = {
val source = positionParams.getTextDocument.getUri.toAbsolutePath
if (source.isScalaFilename) {
if (source.isScalaFilename || source.isJavaFilename) {
val semanticDBDoc =
semanticdbs.textDocument(source).documentIncludingStale
(for {
Expand Down Expand Up @@ -2629,7 +2629,7 @@ class MetalsLanguageServer(
token: CancelToken = EmptyCancelToken
): Future[DefinitionResult] = {
val source = position.getTextDocument.getUri.toAbsolutePath
if (source.isScalaFilename) {
if (source.isScalaFilename || source.isJavaFilename) {
val result =
timerProvider.timedThunk(
"definition",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import scala.collection.mutable.ListBuffer
import scala.meta.internal.bsp.BspSession
import scala.meta.internal.metals.BloopServers
import scala.meta.internal.metals.BuildInfo
import scala.meta.internal.metals.JavaTarget
import scala.meta.internal.metals.Messages
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.metals.ScalaTarget
Expand All @@ -31,6 +32,12 @@ class ProblemResolver(
}
}

def recommendation(java: JavaTarget): String = {
findProblem(java)
.map(_.message)
.getOrElse("")
}

def recommendation(scala: ScalaTarget): String = {
findProblem(scala)
.map(_.message)
Expand All @@ -56,6 +63,7 @@ class ProblemResolver(
case DeprecatedScalaVersion(version) => deprecatedVersions += version
case FutureScalaVersion(version) => futureVersions += version
case _: SemanticDBDisabled => misconfiguredProjects += 1
case _: JavaSemanticDBDisabled => misconfiguredProjects += 1
case _: MissingSourceRoot => misconfiguredProjects += 1
case UnsupportedSbtVersion => unsupportedSbt = true
case DeprecatedSbtVersion => deprecatedSbt = true
Expand Down Expand Up @@ -138,7 +146,6 @@ class ProblemResolver(
}
}

// TODO create a java version of this method to check for semanticDB plugin. Maybe check java version?
private def findProblem(
scalaTarget: ScalaTarget
): Option[ScalaProblem] = {
Expand Down Expand Up @@ -177,4 +184,19 @@ class ProblemResolver(
case _ => None
}
}

private def findProblem(
javaTarget: JavaTarget
): Option[ScalaProblem] = {
if (!javaTarget.isJavaSemanticdbEnabled)
Some(
JavaSemanticDBDisabled(
currentBuildServer().map(_.main.name).getOrElse("<none>"),
isUnsupportedBloopVersion()
)
)
else if (!javaTarget.isJavaSourcerootDeclared)
Some(MissingSourceRoot(workspace.sourcerootOption))
else None
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,21 @@ case class SemanticDBDisabled(
}
}
}
case class JavaSemanticDBDisabled(
bloopVersion: String,
unsupportedBloopVersion: Boolean
) extends ScalaProblem {
override def message: String = {
if (unsupportedBloopVersion) {
s"""|The installed Bloop server version is $bloopVersion while Metals requires at least Bloop version ${BuildInfo.bloopVersion},
|To fix this problem please update your Bloop server.""".stripMargin
} else {
"Semanticdb is required for code navigation to work correctly in your project," +
" however the Java semanticdb plugin doesn't seem to be enabled. " +
"Please enable the semanticdb plugin for this project in order for code navigation to work correctly"
}
}
}
case class MissingSourceRoot(sourcerootOption: String) extends ScalaProblem {
override def message: String =
s"Add the compiler option $sourcerootOption to ensure code navigation works."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ trait CommonMtagsEnrichments {
def isWorksheet: Boolean = {
filename.endsWith(".worksheet.sc")
}
def isJavaFilename: Boolean = {
filename.isJavaFilename
}
def isScalaFilename: Boolean = {
filename.isScalaFilename
}
Expand Down

0 comments on commit 1f8c0c1

Please sign in to comment.