implement window/logMessage

This sends sbt's log message as "window/logMessage" event to LSP.
This commit is contained in:
Eugene Yokota 2017-10-08 01:39:38 -04:00
parent 84dafd0bc0
commit 5c394e18f6
6 changed files with 144 additions and 13 deletions

View File

@ -23,9 +23,10 @@ import scala.util.{ Success, Failure }
import sbt.io.syntax._
import sbt.io.Hash
import sbt.internal.server._
import sbt.internal.util.{ StringEvent, ObjectEvent, ConsoleOut, MainAppender }
import sbt.internal.langserver.{ LogMessageParams, MessageType }
import sbt.internal.util.{ StringEvent, ObjectEvent, MainAppender }
import sbt.internal.util.codec.JValueFormats
import sbt.protocol.{ EventMessage, Serialization, ChannelAcceptedEvent }
import sbt.protocol.{ EventMessage, Serialization }
import sbt.util.{ Level, Logger, LogExchange }
/**
@ -72,7 +73,7 @@ private[sbt] final class CommandExchange {
def run(s: State): State = {
consoleChannel match {
case Some(x) => // do nothing
case Some(_) => // do nothing
case _ =>
val x = new ConsoleChannel("console0")
consoleChannel = Some(x)
@ -116,7 +117,7 @@ private[sbt] final class CommandExchange {
subscribe(channel)
}
server match {
case Some(x) => // do nothing
case Some(_) => // do nothing
case _ =>
val portfile = (new File(".")).getAbsoluteFile / "project" / "target" / "active.json"
val h = Hash.halfHashString(portfile.toURI.toString)
@ -147,13 +148,13 @@ private[sbt] final class CommandExchange {
private[sbt] def notifyEvent[A: JsonFormat](method: String, params: A): Unit = {
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
channels.foreach {
case c: ConsoleChannel =>
case _: ConsoleChannel =>
// c.publishEvent(event)
case c: NetworkChannel =>
try {
c.notifyEvent(method, params)
} catch {
case e: SocketException =>
case _: SocketException =>
toDel += c
}
}
@ -167,21 +168,35 @@ private[sbt] final class CommandExchange {
}
def publishEvent[A: JsonFormat](event: A): Unit = {
val broadcastStringMessage = true
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
event match {
case entry: StringEvent =>
val params = toLogMessageParams(entry)
channels.foreach {
case c: ConsoleChannel =>
if (entry.channelName.isEmpty || entry.channelName == Some(c.name)) {
if (broadcastStringMessage) {
c.publishEvent(event)
} else {
if (entry.channelName.isEmpty || entry.channelName == Some(c.name)) {
c.publishEvent(event)
}
}
case c: NetworkChannel =>
try {
if (entry.channelName == Some(c.name)) {
c.publishEvent(event)
// Note that language server's LogMessageParams does not hold the execid,
// so this is weaker than the StringMessage. We might want to double-send
// in case we have a better client that can utilize the knowledge.
import sbt.internal.langserver.codec.JsonProtocol._
if (broadcastStringMessage) {
c.langNotify("window/logMessage", params)
} else {
if (entry.channelName == Some(c.name)) {
c.langNotify("window/logMessage", params)
}
}
} catch {
case e: SocketException =>
case _: SocketException =>
toDel += c
}
}
@ -193,7 +208,7 @@ private[sbt] final class CommandExchange {
try {
c.publishEvent(event)
} catch {
case e: SocketException =>
case _: SocketException =>
toDel += c
}
}
@ -207,6 +222,10 @@ private[sbt] final class CommandExchange {
}
}
private[sbt] def toLogMessageParams(event: StringEvent): LogMessageParams = {
LogMessageParams(MessageType.fromLevelString(event.level), event.message)
}
/**
* This publishes object events. The type information has been
* erased because it went through logging.
@ -231,7 +250,7 @@ private[sbt] final class CommandExchange {
try {
c.publishObjectEvent(event)
} catch {
case e: SocketException =>
case _: SocketException =>
toDel += c
}
}
@ -265,7 +284,7 @@ private[sbt] final class CommandExchange {
try {
c.publishEventMessage(event)
} catch {
case e: SocketException =>
case _: SocketException =>
toDel += c
}
}

View File

@ -0,0 +1,38 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.langserver
final class LogMessageParams private (
/** The message type. */
val `type`: Long,
/** The actual message */
val message: String) extends Serializable {
override def equals(o: Any): Boolean = o match {
case x: LogMessageParams => (this.`type` == x.`type`) && (this.message == x.message)
case _ => false
}
override def hashCode: Int = {
37 * (37 * (37 * (17 + "sbt.internal.langserver.LogMessageParams".##) + `type`.##) + message.##)
}
override def toString: String = {
"LogMessageParams(" + `type` + ", " + message + ")"
}
protected[this] def copy(`type`: Long = `type`, message: String = message): LogMessageParams = {
new LogMessageParams(`type`, message)
}
def withType(`type`: Long): LogMessageParams = {
copy(`type` = `type`)
}
def withMessage(message: String): LogMessageParams = {
copy(message = message)
}
}
object LogMessageParams {
def apply(`type`: Long, message: String): LogMessageParams = new LogMessageParams(`type`, message)
}

View File

@ -16,6 +16,7 @@ trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.internal.langserver.codec.TextDocumentSyncOptionsFormats
with sbt.internal.langserver.codec.ServerCapabilitiesFormats
with sbt.internal.langserver.codec.InitializeResultFormats
with sbt.internal.langserver.codec.LogMessageParamsFormats
with sbt.internal.langserver.codec.PublishDiagnosticsParamsFormats
with sbt.internal.langserver.codec.SbtExecParamsFormats
object JsonProtocol extends JsonProtocol

View File

@ -0,0 +1,29 @@
/**
* This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]].
*/
// DO NOT EDIT MANUALLY
package sbt.internal.langserver.codec
import _root_.sjsonnew.{ Unbuilder, Builder, JsonFormat, deserializationError }
trait LogMessageParamsFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val LogMessageParamsFormat: JsonFormat[sbt.internal.langserver.LogMessageParams] = new JsonFormat[sbt.internal.langserver.LogMessageParams] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.internal.langserver.LogMessageParams = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val `type` = unbuilder.readField[Long]("type")
val message = unbuilder.readField[String]("message")
unbuilder.endObject()
sbt.internal.langserver.LogMessageParams(`type`, message)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.internal.langserver.LogMessageParams, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("type", obj.`type`)
builder.addField("message", obj.message)
builder.endObject()
}
}
}

View File

@ -98,6 +98,16 @@ type SaveOptions {
includeText: Boolean
}
# LogMessage Notification
type LogMessageParams {
## The message type.
type: Long!
## The actual message
message: String!
}
# Document
# PublishDiagnostics Notification https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#textDocument_publishDiagnostics

View File

@ -0,0 +1,34 @@
/*
* sbt
* Copyright 2011 - 2017, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under BSD-3-Clause license (see LICENSE)
*/
package sbt
package internal
package langserver
object MessageType {
/** An error message. */
val Error = 1L
/** A warning message. */
val Warning = 2L
/** An information message. */
val Info = 3L
/** A log message. */
val Log = 4L
def fromLevelString(level: String): Long = {
level.toLowerCase match {
case "debug" => Log
case "info" => Info
case "warn" => Warning
case "error" => Error
}
}
}