summaryrefslogtreecommitdiff
path: root/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala')
-rw-r--r--contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala138
1 files changed, 138 insertions, 0 deletions
diff --git a/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala
new file mode 100644
index 00000000..d1b9a66b
--- /dev/null
+++ b/contrib/bsp/src/mill/contrib/bsp/BspLoggedReporter.scala
@@ -0,0 +1,138 @@
+package mill.contrib.bsp
+
+import java.io.File
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicInteger
+
+import ch.epfl.scala.bsp4j._
+import ch.epfl.scala.{bsp4j => bsp}
+import mill.api.{BuildProblemReporter, Problem}
+
+import scala.collection.JavaConverters._
+import scala.collection.concurrent
+import scala.language.implicitConversions
+
+/**
+ * Specialized reporter that sends compilation diagnostics
+ * for each problem it logs, either as information, warning or
+ * error as well as task finish notifications of type `compile-report`.
+ *
+ * @param client the client to send diagnostics to
+ * @param targetId the target id of the target whose compilation
+ * the diagnostics are related to
+ * @param taskId a unique id of the compilation task of the target
+ * specified by `targetId`
+ * @param compilationOriginId optional origin id the client assigned to
+ * the compilation request. Needs to be sent
+ * back as part of the published diagnostics
+ * as well as compile report
+ */
+class BspLoggedReporter(client: bsp.BuildClient,
+ targetId: BuildTargetIdentifier,
+ taskId: TaskId,
+ compilationOriginId: Option[String]) extends BuildProblemReporter {
+
+ var errors = new AtomicInteger(0)
+ var warnings = new AtomicInteger(0)
+ var infos = new AtomicInteger(0)
+ var diagnosticMap: concurrent.Map[TextDocumentIdentifier, bsp.PublishDiagnosticsParams] =
+ new ConcurrentHashMap[TextDocumentIdentifier, bsp.PublishDiagnosticsParams]().asScala
+
+ override def logError(problem: Problem): Unit = {
+ client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId))
+ errors.incrementAndGet()
+ }
+
+ override def logInfo(problem: Problem): Unit = {
+ client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId))
+ infos.incrementAndGet()
+ }
+
+ // Obtains the parameters for sending diagnostics about the given Problem ( as well as
+ // about all previous problems generated for the same text file ) related to the specified
+ // targetId, incorporating the given optional originId ( generated by the client for the
+ // compile request )
+ //TODO: document that if the problem is a general information without a text document
+ // associated to it, then the document field of the diagnostic is set to the uri of the target
+ private[this] def getDiagnostics(problem: Problem, targetId: bsp.BuildTargetIdentifier, originId: Option[String]):
+ bsp.PublishDiagnosticsParams = {
+ val diagnostic = getSingleDiagnostic(problem)
+ val sourceFile = problem.position.sourceFile
+ val textDocument = new TextDocumentIdentifier(
+ sourceFile.getOrElse(None) match {
+ case None => targetId.getUri
+ case f: File => f.toURI.toString
+ })
+ val params = new bsp.PublishDiagnosticsParams(textDocument,
+ targetId,
+ appendDiagnostics(textDocument,
+ diagnostic).asJava,
+ true)
+
+ if (originId.nonEmpty) {
+ params.setOriginId(originId.get)
+ }
+ diagnosticMap.put(textDocument, params)
+ params
+ }
+
+ // Update the published diagnostics for the fiven text file by
+ // adding the recently computed diagnostic to the list of
+ // all previous diagnostics generated for the same file.
+ private[this] def appendDiagnostics(textDocument: TextDocumentIdentifier,
+ currentDiagnostic: Diagnostic): List[Diagnostic] = {
+ diagnosticMap.putIfAbsent(textDocument, new bsp.PublishDiagnosticsParams(
+ textDocument,
+ targetId,
+ List.empty[Diagnostic].asJava, true))
+ diagnosticMap(textDocument).getDiagnostics.asScala.toList ++ List(currentDiagnostic)
+ }
+
+ // Computes the diagnostic related to the given Problem
+ private[this] def getSingleDiagnostic(problem: Problem): Diagnostic = {
+ val pos = problem.position
+ val i: Integer = pos.startLine.orElse(pos.line).getOrElse[Int](0)
+ println(i)
+ val start = new bsp.Position(
+ pos.startLine.orElse(pos.line).getOrElse[Int](0),
+ pos.startOffset.orElse(pos.offset).getOrElse[Int](0)
+ )
+ val end = new bsp.Position(
+ pos.endLine.orElse(pos.line).getOrElse[Int](start.getLine.intValue()),
+ pos.endOffset.orElse(pos.offset).getOrElse[Int](start.getCharacter.intValue()))
+ val diagnostic = new bsp.Diagnostic(new bsp.Range(start, end), problem.message)
+ diagnostic.setCode(pos.lineContent)
+ diagnostic.setSource("compiler from mill")
+ diagnostic.setSeverity(problem.severity match {
+ case mill.api.Info => bsp.DiagnosticSeverity.INFORMATION
+ case mill.api.Error => bsp.DiagnosticSeverity.ERROR
+ case mill.api.Warn => bsp.DiagnosticSeverity.WARNING
+ }
+ )
+ diagnostic
+ }
+
+ override def logWarning(problem: Problem): Unit = {
+ client.onBuildPublishDiagnostics(getDiagnostics(problem, targetId, compilationOriginId))
+ warnings.incrementAndGet()
+ }
+
+ override def printSummary(): Unit = {
+ val taskFinishParams = new TaskFinishParams(taskId, getStatusCode)
+ taskFinishParams.setEventTime(System.currentTimeMillis())
+ taskFinishParams.setMessage("Finished compiling target: " + targetId.getUri)
+ taskFinishParams.setDataKind("compile-report")
+ val compileReport = new CompileReport(targetId, errors.get, warnings.get)
+ compilationOriginId match {
+ case Some(id) => compileReport.setOriginId(id)
+ case None =>
+ }
+ taskFinishParams.setData(compileReport)
+ client.onBuildTaskFinish(taskFinishParams)
+ }
+
+ // Compute the compilation status code
+ private[this] def getStatusCode: StatusCode = {
+ if (errors.get > 0) StatusCode.ERROR else StatusCode.OK
+ }
+}