diff --git a/modules/app/src/main/scala/LangoustineApp.scala b/modules/app/src/main/scala/LangoustineApp.scala index 776289cb3..d4e8040d0 100644 --- a/modules/app/src/main/scala/LangoustineApp.scala +++ b/modules/app/src/main/scala/LangoustineApp.scala @@ -150,16 +150,7 @@ object LangoustineApp: .concurrently( in .through(lsp.decodeMessages) - .evalMap { - case Left(err) => IO.pure(None) // TODO - // Logging - // .error( - // s"Failed to decode message from target LSP's stdout: $err" - // ) - // .as(None) - case Right(payload) => IO.pure(Some(payload)) - } - .unNone + .evalMap(IO.fromEither) .through(channel.input) ) .concurrently( diff --git a/modules/tests/src/test/scalajvm/tracer/Feed.scala b/modules/tests/src/test/scalajvm/tracer/Feed.scala index 922313c11..345cb46c0 100644 --- a/modules/tests/src/test/scalajvm/tracer/Feed.scala +++ b/modules/tests/src/test/scalajvm/tracer/Feed.scala @@ -37,8 +37,6 @@ case class Feed( case Some(value) => f(this).publish1(value) end match - - // f(this).publish1(Payload(ser.getBytes())) end send def send(f: this.type => Topic[IO, Chunk[Byte]], str: String) = diff --git a/modules/tracer/shared/src/main/scala/Direction.scala b/modules/tracer/shared/src/main/scala/Direction.scala new file mode 100644 index 000000000..3aae01508 --- /dev/null +++ b/modules/tracer/shared/src/main/scala/Direction.scala @@ -0,0 +1,17 @@ +package langoustine.tracer + +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import scala.util.Try +import jsonrpclib.{Payload, ErrorPayload} +import jsonrpclib.CallId +import jsonrpclib.InputMessage.* +import jsonrpclib.OutputMessage.* +import jsonrpclib.Message + +enum Direction: + case ToServer, ToClient + + def reverse: Direction = this match + case ToServer => ToClient + case ToClient => ToServer diff --git a/modules/tracer/shared/src/main/scala/LogMessage.scala b/modules/tracer/shared/src/main/scala/LogMessage.scala new file mode 100644 index 000000000..3844e8a6f --- /dev/null +++ b/modules/tracer/shared/src/main/scala/LogMessage.scala @@ -0,0 +1,20 @@ +package langoustine.tracer + +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import scala.util.Try +import jsonrpclib.{Payload, ErrorPayload} +import jsonrpclib.CallId +import jsonrpclib.InputMessage.* +import jsonrpclib.OutputMessage.* +import jsonrpclib.Message + +enum LogMessage(val value: String): + case Window(override val value: String, timestamp: Long) + extends LogMessage(value) + case Stderr(override val value: String, timestamp: Long) + extends LogMessage(value) + +object LogMessage: + given JsonValueCodec[LogMessage] = JsonCodecMaker.make + given JsonValueCodec[Vector[LogMessage]] = JsonCodecMaker.make diff --git a/modules/tracer/shared/src/main/scala/LspMessage.scala b/modules/tracer/shared/src/main/scala/LspMessage.scala new file mode 100644 index 000000000..9aa464d46 --- /dev/null +++ b/modules/tracer/shared/src/main/scala/LspMessage.scala @@ -0,0 +1,58 @@ +package langoustine.tracer + +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import scala.util.Try +import jsonrpclib.{Payload, ErrorPayload} +import jsonrpclib.CallId +import jsonrpclib.InputMessage.* +import jsonrpclib.OutputMessage.* +import jsonrpclib.Message + +enum LspMessage(val id: CallId): + case Request( + method: String, + override val id: CallId, + responded: Boolean, + direction: Direction + ) extends LspMessage(id) + + case Response( + override val id: CallId, + method: Option[String], + direction: Direction + ) extends LspMessage(id) + + case Notification( + generatedId: CallId, + method: String, + direction: Direction + ) extends LspMessage(generatedId) + + def methodName: Option[String] = this match + case r: Request => Some(r.method) + case r: Notification => Some(r.method) + case r: Response => r.method +end LspMessage + +object LspMessage: + given JsonValueCodec[LspMessage] = JsonCodecMaker.make + + def from( + raw: RawMessage, + direction: Direction, + generatedId: CallId + ): Option[LspMessage] = + raw.id match + // notification + case None => + raw.method.map( + LspMessage.Notification(generatedId, _, direction) + ) + case Some(id) => // it's a request/response + raw.method match + case None => + Some(LspMessage.Response(id, None, direction)) + case Some(value) => + Some(LspMessage.Request(value, id, responded = false, direction)) +end LspMessage diff --git a/modules/tracer/shared/src/main/scala/Protocol.scala b/modules/tracer/shared/src/main/scala/Protocol.scala deleted file mode 100644 index 02bda46c5..000000000 --- a/modules/tracer/shared/src/main/scala/Protocol.scala +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2022 Neandertech - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package langoustine.tracer - -import com.github.plokhotnyuk.jsoniter_scala.core.* -import com.github.plokhotnyuk.jsoniter_scala.macros.* - -import scala.util.Try -import jsonrpclib.{Payload, ErrorPayload} -import jsonrpclib.CallId -import jsonrpclib.InputMessage.* -import jsonrpclib.OutputMessage.* -import jsonrpclib.Message - -case class ReceivedMessage( - timestamp: Long, - raw: RawMessage, - decoded: LspMessage -) - -object ReceivedMessage: - given JsonValueCodec[ReceivedMessage] = JsonCodecMaker.make - -case class RawMessage( - jsonrpc: String, - method: Option[String] = None, - result: Option[Payload] = None, - error: Option[ErrorPayload] = None, - params: Option[Payload] = None, - id: Option[CallId] = None -): - def toMessage: Option[Message] = - id match - // notification - case None => - method.map( - // LspMessage.Notification(generatedId, _, direction) - NotificationMessage(_, params) - ) - case Some(id) => // it's a request/response - method match - case None => - error - .map(ErrorMessage(id, _)) - .orElse( - result.map( - ResponseMessage(id, _) - ) - ) // LspMessage.Response(id, None, direction)) - case Some(value) => - Some(RequestMessage(value, id, params)) - // Some(LspMessage.Request(value, id, responded = false, direction)) -end RawMessage - -object RawMessage: - import com.github.plokhotnyuk.jsoniter_scala.macros.* - - given JsonValueCodec[RawMessage] = JsonCodecMaker.make - - def create( - method: Option[String] = None, - result: Option[Payload] = None, - error: Option[ErrorPayload] = None, - params: Option[Payload] = None, - id: Option[CallId] = None - ) = - RawMessage("2.0", method, result, error, params, id) -end RawMessage - -enum Direction: - case ToServer, ToClient - - def reverse: Direction = this match - case ToServer => ToClient - case ToClient => ToServer - -enum LogMessage(val value: String): - case Window(override val value: String, timestamp: Long) - extends LogMessage(value) - case Stderr(override val value: String, timestamp: Long) - extends LogMessage(value) - -object LogMessage: - given JsonValueCodec[LogMessage] = JsonCodecMaker.make - given JsonValueCodec[Vector[LogMessage]] = JsonCodecMaker.make - -enum TracerEvent: - case LogLines(lines: Vector[LogMessage]) - case Update - -object TracerEvent: - given JsonValueCodec[TracerEvent] = JsonCodecMaker.make - -enum LspMessage(val id: CallId): - case Request( - method: String, - override val id: CallId, - responded: Boolean, - direction: Direction - ) extends LspMessage(id) - - case Response( - override val id: CallId, - method: Option[String], - direction: Direction - ) extends LspMessage(id) - - case Notification( - generatedId: CallId, - method: String, - direction: Direction - ) extends LspMessage(generatedId) - - def methodName: Option[String] = this match - case r: Request => Some(r.method) - case r: Notification => Some(r.method) - case r: Response => r.method - -end LspMessage - -object LspMessage: - given JsonValueCodec[LspMessage] = JsonCodecMaker.make - - def from( - raw: RawMessage, - direction: Direction, - generatedId: CallId - ): Option[LspMessage] = - raw.id match - // notification - case None => - raw.method.map( - LspMessage.Notification(generatedId, _, direction) - ) - case Some(id) => // it's a request/response - raw.method match - case None => - Some(LspMessage.Response(id, None, direction)) - case Some(value) => - Some(LspMessage.Request(value, id, responded = false, direction)) - -end LspMessage - -// enum MessageId: -// case StringId(id: String) -// case NumberId(id: Long) -// case NullId - -// object MessageId: -// given JsonValueCodec[MessageId] = new JsonValueCodec[MessageId]: -// def decodeValue(in: JsonReader, default: MessageId): MessageId = -// Try(in.readLong()) -// .map(MessageId.NumberId.apply) -// .orElse(Try { -// in.rollbackToken() -// in.readString(null) -// }.map(MessageId.StringId.apply)) -// .orElse(scala.util.Success(default)) -// .get - -// import MessageId.* - -// def encodeValue(x: MessageId, out: JsonWriter): Unit = x match -// case NumberId(long) => out.writeVal(long) -// case StringId(string) => out.writeVal(string) -// case NullId => out.writeNull() - -// def nullValue: MessageId = MessageId.NullId -// end MessageId - -enum Summary: - case Trace(workingFolder: String, serverCommand: List[String]) - case Replay(file: String) -object Summary: - given JsonValueCodec[Summary] = JsonCodecMaker.make diff --git a/modules/tracer/shared/src/main/scala/RawMessage.scala b/modules/tracer/shared/src/main/scala/RawMessage.scala new file mode 100644 index 000000000..b74606e06 --- /dev/null +++ b/modules/tracer/shared/src/main/scala/RawMessage.scala @@ -0,0 +1,54 @@ +package langoustine.tracer + +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import scala.util.Try +import jsonrpclib.{Payload, ErrorPayload} +import jsonrpclib.CallId +import jsonrpclib.InputMessage.* +import jsonrpclib.OutputMessage.* +import jsonrpclib.Message + +case class RawMessage( + jsonrpc: String, + method: Option[String] = None, + result: Option[Payload] = None, + error: Option[ErrorPayload] = None, + params: Option[Payload] = None, + id: Option[CallId] = None +): + def toMessage: Option[Message] = + id match + // notification + case None => + method.map( + NotificationMessage(_, params) + ) + case Some(id) => // it's a request/response + method match + case None => + error + .map(ErrorMessage(id, _)) + .orElse( + result.map( + ResponseMessage(id, _) + ) + ) + case Some(value) => + Some(RequestMessage(value, id, params)) +end RawMessage + +object RawMessage: + import com.github.plokhotnyuk.jsoniter_scala.macros.* + + given JsonValueCodec[RawMessage] = JsonCodecMaker.make + + def create( + method: Option[String] = None, + result: Option[Payload] = None, + error: Option[ErrorPayload] = None, + params: Option[Payload] = None, + id: Option[CallId] = None + ) = + RawMessage("2.0", method, result, error, params, id) +end RawMessage diff --git a/modules/tracer/shared/src/main/scala/ReceivedMessage.scala b/modules/tracer/shared/src/main/scala/ReceivedMessage.scala new file mode 100644 index 000000000..ae20afaea --- /dev/null +++ b/modules/tracer/shared/src/main/scala/ReceivedMessage.scala @@ -0,0 +1,19 @@ +package langoustine.tracer + +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import scala.util.Try +import jsonrpclib.{Payload, ErrorPayload} +import jsonrpclib.CallId +import jsonrpclib.InputMessage.* +import jsonrpclib.OutputMessage.* +import jsonrpclib.Message + +case class ReceivedMessage( + timestamp: Long, + raw: RawMessage, + decoded: LspMessage +) + +object ReceivedMessage: + given JsonValueCodec[ReceivedMessage] = JsonCodecMaker.make diff --git a/modules/tracer/shared/src/main/scala/Summary.scala b/modules/tracer/shared/src/main/scala/Summary.scala new file mode 100644 index 000000000..604efbb5f --- /dev/null +++ b/modules/tracer/shared/src/main/scala/Summary.scala @@ -0,0 +1,17 @@ +package langoustine.tracer + +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import scala.util.Try +import jsonrpclib.{Payload, ErrorPayload} +import jsonrpclib.CallId +import jsonrpclib.InputMessage.* +import jsonrpclib.OutputMessage.* +import jsonrpclib.Message + +enum Summary: + case Trace(workingFolder: String, serverCommand: List[String]) + case Replay(file: String) + +object Summary: + given JsonValueCodec[Summary] = JsonCodecMaker.make diff --git a/modules/tracer/shared/src/main/scala/TracerEvent.scala b/modules/tracer/shared/src/main/scala/TracerEvent.scala new file mode 100644 index 000000000..5a47f7276 --- /dev/null +++ b/modules/tracer/shared/src/main/scala/TracerEvent.scala @@ -0,0 +1,17 @@ +package langoustine.tracer + +import com.github.plokhotnyuk.jsoniter_scala.core.* +import com.github.plokhotnyuk.jsoniter_scala.macros.* +import scala.util.Try +import jsonrpclib.{Payload, ErrorPayload} +import jsonrpclib.CallId +import jsonrpclib.InputMessage.* +import jsonrpclib.OutputMessage.* +import jsonrpclib.Message + +enum TracerEvent: + case LogLines(lines: Vector[LogMessage]) + case Update + +object TracerEvent: + given JsonValueCodec[TracerEvent] = JsonCodecMaker.make