mirror of https://github.com/sbt/sbt.git
Merge pull request #3740 from laughedelic/wip/logmessage
LSP: improve logMessage notifications
This commit is contained in:
commit
e6165464ba
|
|
@ -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, ExecStatusEvent }
|
||||
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,33 +168,48 @@ 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 =>
|
||||
channels.foreach {
|
||||
val params = toLogMessageParams(entry)
|
||||
channels collect {
|
||||
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
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
channels.foreach {
|
||||
channels collect {
|
||||
case c: ConsoleChannel =>
|
||||
c.publishEvent(event)
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
c.publishEvent(event)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
case _: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
|
|
@ -207,6 +223,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.
|
||||
|
|
@ -224,14 +244,14 @@ private[sbt] final class CommandExchange {
|
|||
JField("execId", JString(execId))
|
||||
})): _*
|
||||
)
|
||||
channels.foreach {
|
||||
channels collect {
|
||||
case c: ConsoleChannel =>
|
||||
c.publishEvent(json)
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
c.publishObjectEvent(event)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
case _: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
|
|
@ -249,23 +269,39 @@ private[sbt] final class CommandExchange {
|
|||
val toDel: ListBuffer[CommandChannel] = ListBuffer.empty
|
||||
event match {
|
||||
// Special treatment for ConsolePromptEvent since it's hand coded without codec.
|
||||
case e: ConsolePromptEvent =>
|
||||
case entry: ConsolePromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEventMessage(e)
|
||||
case c: ConsoleChannel => c.publishEventMessage(entry)
|
||||
}
|
||||
case e: ConsoleUnpromptEvent =>
|
||||
case entry: ConsoleUnpromptEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel => c.publishEventMessage(e)
|
||||
case c: ConsoleChannel => c.publishEventMessage(entry)
|
||||
}
|
||||
case entry: ExecStatusEvent =>
|
||||
channels collect {
|
||||
case c: ConsoleChannel =>
|
||||
if (entry.channelName.isEmpty || entry.channelName == Some(c.name)) {
|
||||
c.publishEventMessage(event)
|
||||
}
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
if (entry.channelName == Some(c.name)) {
|
||||
c.publishEventMessage(event)
|
||||
}
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
channels.foreach {
|
||||
channels collect {
|
||||
case c: ConsoleChannel =>
|
||||
c.publishEventMessage(event)
|
||||
case c: NetworkChannel =>
|
||||
try {
|
||||
c.publishEventMessage(event)
|
||||
} catch {
|
||||
case e: SocketException =>
|
||||
case _: SocketException =>
|
||||
toDel += c
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,14 @@ private[sbt] trait LanguageServerProtocol extends CommandChannel {
|
|||
publishBytes(bytes)
|
||||
}
|
||||
|
||||
def logMessage(level: String, message: String): Unit = {
|
||||
import sbt.internal.langserver.codec.JsonProtocol._
|
||||
langNotify(
|
||||
"window/logMessage",
|
||||
LogMessageParams(MessageType.fromLevelString(level), message)
|
||||
)
|
||||
}
|
||||
|
||||
private[sbt] lazy val serverCapabilities: ServerCapabilities = {
|
||||
ServerCapabilities(textDocumentSync =
|
||||
TextDocumentSyncOptions(true, 0, false, false, SaveOptions(false)),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import sjsonnew._
|
|||
import scala.annotation.tailrec
|
||||
import sbt.protocol._
|
||||
import sbt.internal.langserver.ErrorCodes
|
||||
import sbt.internal.util.ObjectEvent
|
||||
import sbt.internal.util.{ ObjectEvent, StringEvent }
|
||||
import sbt.internal.util.codec.JValueFormats
|
||||
import sbt.util.Logger
|
||||
|
||||
|
|
@ -227,7 +227,10 @@ final class NetworkChannel(val name: String,
|
|||
|
||||
def publishEvent[A: JsonFormat](event: A, execId: Option[String]): Unit = {
|
||||
if (isLanguageServerProtocol) {
|
||||
langRespond(event, execId)
|
||||
event match {
|
||||
case entry: StringEvent => logMessage(entry.level, entry.message)
|
||||
case _ => langRespond(event, execId)
|
||||
}
|
||||
} else {
|
||||
contentType match {
|
||||
case SbtX1Protocol =>
|
||||
|
|
@ -241,11 +244,19 @@ final class NetworkChannel(val name: String,
|
|||
def publishEvent[A: JsonFormat](event: A): Unit = publishEvent(event, None)
|
||||
|
||||
def publishEventMessage(event: EventMessage): Unit = {
|
||||
contentType match {
|
||||
case SbtX1Protocol =>
|
||||
val bytes = Serialization.serializeEventMessage(event)
|
||||
publishBytes(bytes, true)
|
||||
case _ =>
|
||||
if (isLanguageServerProtocol) {
|
||||
event match {
|
||||
case entry: LogEvent => logMessage(entry.level, entry.message)
|
||||
case entry: ExecStatusEvent => logMessage("debug", entry.status)
|
||||
case _ => ()
|
||||
}
|
||||
} else {
|
||||
contentType match {
|
||||
case SbtX1Protocol =>
|
||||
val bytes = Serialization.serializeEventMessage(event)
|
||||
publishBytes(bytes, true)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 "info" => Info
|
||||
case "warn" => Warning
|
||||
case "error" => Error
|
||||
case _ => Log
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue