Skip to content
This repository was archived by the owner on Jun 8, 2023. It is now read-only.

Commit 1b69898

Browse files
author
fc
committed
unpackRequest
1 parent 17f7327 commit 1b69898

File tree

11 files changed

+122
-57
lines changed

11 files changed

+122
-57
lines changed

build.sbt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
name := "vscode-scala"
22

33

4-
scalaVersion in ThisBuild := "2.11.11"
4+
scalaVersion in ThisBuild := "2.12.3"
55

66
publishMavenStyle := true
77
publishArtifact in Test := false
88
pomIncludeRepository := { _ => false }
99

1010
lazy val commonSettings = Seq(
1111
organization := "com.github.dragos",
12-
version := "0.1.4-SNAPSHOT",
12+
version := "0.1.7-SNAPSHOT",
1313
resolvers += "dhpcs at bintray" at "https://dl.bintray.com/dhpcs/maven",
1414
libraryDependencies ++= Seq(
15-
"org.scalatest" %% "scalatest" % "2.2.6" % "test"
15+
"org.scalatest" %% "scalatest" % "3.0.1" % "test"
1616
),
1717
publishTo := {
1818
val nexus = "https://oss.sonatype.org/"
@@ -27,8 +27,8 @@ lazy val languageserver = project.
2727
settings(commonSettings).
2828
settings(
2929
libraryDependencies ++= Seq(
30-
"com.dhpcs" %% "play-json-rpc" % "1.3.0",
31-
"com.typesafe.scala-logging" %% "scala-logging" % "3.4.0",
30+
"com.dhpcs" %% "scala-json-rpc" % "2.0.0",
31+
"com.typesafe.scala-logging" %% "scala-logging" % "3.7.2",
3232
"org.slf4j" % "slf4j-api" % "1.7.21",
3333
"ch.qos.logback" % "logback-classic" % "1.1.7",
3434
"org.codehaus.groovy" % "groovy" % "2.4.0"
@@ -63,7 +63,7 @@ lazy val `ensime-lsp` = project.
6363
settings(
6464
resolvers += Resolver.sonatypeRepo("snapshots"),
6565
libraryDependencies ++= Seq(
66-
"org.ensime" %% "core" % "2.0.0-M3"
66+
"org.ensime" %% "core" % "2.0.0-M4"
6767
),
6868
pomExtra in Global := {
6969
<url>https://github.com/dragos/dragos-vscode-scala/</url>

ensime-lsp/src/main/scala/org/github/dragos/vscode/EnsimeLanguageServer.scala

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ import org.ensime.config.EnsimeConfigProtocol
1818
import org.ensime.core._
1919
import org.ensime.util.file._
2020
import org.ensime.util.path._
21-
21+
import org.ensime.config._
22+
import org.ensime.config.richconfig._
23+
import org.ensime.core.Canon
24+
import com.typesafe.config._
2225
import akka.actor.ActorRef
2326
import akka.actor.ActorSystem
2427
import akka.actor.Props
@@ -37,7 +40,7 @@ import java.nio.charset.Charset
3740
import langserver.core.MessageReader
3841

3942
class EnsimeLanguageServer(in: InputStream, out: OutputStream) extends LanguageServer(in, out) {
40-
private val system = ActorSystem("ENSIME")
43+
private var system:ActorSystem = _
4144
private var fileStore: TempFileStore = _
4245

4346
// Ensime root actor
@@ -80,29 +83,48 @@ class EnsimeLanguageServer(in: InputStream, out: OutputStream) extends LanguageS
8083
)
8184
}
8285

86+
def loadConfig(ensimeFile:File): Config = {
87+
val config = s"""ensime.config = "${ensimeFile.toString}" """
88+
val fallback = ConfigFactory.parseString(config)
89+
ConfigFactory.load().withFallback(fallback)
90+
}
91+
8392
private def initializeEnsime(rootPath: String): Try[EnsimeConfig] = {
8493
val ensimeFile = new File(s"$rootPath/.ensime")
8594

8695
val configT = Try {
87-
EnsimeConfigProtocol.parse(ensimeFile.toPath.readString()(MessageReader.Utf8Charset))
96+
val config = loadConfig(ensimeFile)
97+
system = ActorSystem("ENSIME", config)
98+
val serverConfig: EnsimeServerConfig = parseServerConfig(config)
99+
val ensimeConfig = EnsimeConfigProtocol.parse(serverConfig.config.file.readString()(MessageReader.Utf8Charset))
100+
Canon.config = ensimeConfig
101+
Canon.serverConfig = serverConfig
102+
(ensimeConfig, serverConfig)
88103
}
89104

105+
configT.recover{case e => logger.error(s"initializeEnsime Error: ${e.getMessage}"); e.printStackTrace}
106+
90107
configT match {
91108
case Failure(e) =>
92109
if (ensimeFile.exists)
93110
connection.showMessage(MessageType.Error, s"Error parsing .ensime: ${e.getMessage}")
94111
else
95112
connection.showMessage(MessageType.Error, s"No .ensime file in directory. Run `sbt ensimeConfig` to create one.")
96-
case Success(config) =>
113+
case Success((config, serverConfig)) =>
97114
//showMessage(MessageType.Info, s"Using configuration: $ensimeFile")
98115
logger.info(s"Using configuration: $config")
99-
fileStore = new TempFileStore(config.cacheDir.toString)
100-
ensimeActor = system.actorOf(Props(classOf[EnsimeActor], this, config), "server")
101-
102-
// we don't give a damn about them, but Ensime expects it
116+
val t = Try{
117+
fileStore = new TempFileStore(config.cacheDir.file.toString)
118+
ensimeActor = system.actorOf(Props(classOf[EnsimeActor], this, config, serverConfig), "server")
119+
}
120+
t.recover{case e =>
121+
logger.error(s"initializeEnsime: ${e.getMessage}"); e.printStackTrace
122+
connection.showMessage(MessageType.Error, s"Error creating storage: ${e.getMessage}")
123+
}
124+
// we don't give a damn about them, but Ensime expects it
103125
ensimeActor ! ConnectionInfoReq
104126
}
105-
configT
127+
configT.map(_._1)
106128
}
107129

108130
override def onChangeWatchedFiles(changes: Seq[FileEvent]): Unit = changes match {
@@ -194,7 +216,7 @@ class EnsimeLanguageServer(in: InputStream, out: OutputStream) extends LanguageS
194216
res.map(f => CompletionList(false, Await.result(f, 5 seconds))) getOrElse CompletionList(false, Nil)
195217
}
196218

197-
override def gotoDefinitionRequest(textDocument: TextDocumentIdentifier, position: Position): Seq[Location] = {
219+
override def gotoDefinitionRequest(textDocument: TextDocumentIdentifier, position: Position): LocationSeq = {
198220
import scala.concurrent.ExecutionContext.Implicits._
199221
logger.info(s"Got goto definition request at (${position.line}, ${position.character}).")
200222

@@ -230,7 +252,8 @@ class EnsimeLanguageServer(in: InputStream, out: OutputStream) extends LanguageS
230252
}
231253
}
232254

233-
res.map { f => Await.result(f, 5 seconds) } getOrElse Seq.empty[Location]
255+
val r = (res.map { f => Await.result(f, 5 seconds) } getOrElse Seq.empty[Location])
256+
LocationSeq(r)
234257
}
235258

236259
override def hoverRequest(textDocument: TextDocumentIdentifier, position: Position): Hover = {

ensime-lsp/src/main/scala/org/github/dragos/vscode/Main.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ object Main extends LazyLogging {
1010
val cwd = System.getProperty("vscode.workspace")
1111
logger.info(s"Starting server in $cwd")
1212
logger.info(s"Classpath: ${Properties.javaClassPath}")
13+
logger.info(s"LogLevel: ${Option(System.getProperty("vscode.logLevel")).getOrElse("")}")
1314

1415
val server = new EnsimeLanguageServer(System.in, System.out)
1516

@@ -25,6 +26,19 @@ object Main extends LazyLogging {
2526
System.setOut(origOut)
2627
}
2728

29+
logger.underlying match {
30+
case logbackLogger:ch.qos.logback.classic.Logger =>
31+
val logLevel = Option(System.getProperty("vscode.logLevel"))
32+
logLevel match{
33+
case Some("ERROR") => logbackLogger.setLevel(ch.qos.logback.classic.Level.ERROR)
34+
case Some("INFO") => logbackLogger.setLevel(ch.qos.logback.classic.Level.INFO)
35+
case Some("DEBUG") => logbackLogger.setLevel(ch.qos.logback.classic.Level.DEBUG)
36+
case Some("WARN") => logbackLogger.setLevel(ch.qos.logback.classic.Level.WARN)
37+
case _ =>
38+
}
39+
case _ =>
40+
}
41+
2842
// make sure we actually exit
2943
System.exit(0)
3044
}

ensime-lsp/src/main/scala/org/github/dragos/vscode/ensime/EnsimeActor.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.github.dragos.vscode.ensime
22

33
import akka.actor._
44
import org.ensime.api.EnsimeConfig
5+
import org.ensime.api.EnsimeServerConfig
56
import org.github.dragos.vscode.EnsimeLanguageServer
67
import langserver.messages.MessageType
78
import com.typesafe.scalalogging.LazyLogging
@@ -12,7 +13,7 @@ import scala.concurrent.duration._
1213
*
1314
* It catches `ActorInitializationError` and tries to restart it.
1415
*/
15-
class EnsimeActor(langServer: EnsimeLanguageServer, config: EnsimeConfig) extends Actor with LazyLogging {
16+
class EnsimeActor(langServer: EnsimeLanguageServer, config: EnsimeConfig, ensimeServerConfig:EnsimeServerConfig) extends Actor with LazyLogging {
1617

1718
private var project: ActorRef = _
1819

@@ -32,10 +33,10 @@ class EnsimeActor(langServer: EnsimeLanguageServer, config: EnsimeConfig) extend
3233
// trying to catch this elusive ActorInitializationException by creating this actor as late
3334
// as possible. Still, it looks like the supervisor strategy does not get this crash
3435
logger.info("Starting problematic actor now")
35-
project = context.actorOf(Props(new EnsimeProjectServer(langServer, config)), "ensimeProject")
36+
project = context.actorOf(Props(new EnsimeProjectServer(langServer, config, ensimeServerConfig)), "ensimeProject")
3637
logger.info(s"Created: $project")
3738
}
38-
39+
3940
project forward message
4041
}
4142
}

ensime-lsp/src/main/scala/org/github/dragos/vscode/ensime/EnsimeProjectServer.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import org.ensime.api._
77
import org.ensime.api.EnsimeConfig
88
import org.ensime.core.Broadcaster
99
import org.ensime.core.Project
10-
10+
import org.ensime.api.EnsimeServerConfig
1111
import com.typesafe.scalalogging.LazyLogging
1212

1313
import akka.actor.Actor
@@ -19,7 +19,9 @@ import langserver.messages.MessageType
1919
import org.github.dragos.vscode.EnsimeLanguageServer
2020
import akka.actor.TypedActor.PostStop
2121

22-
class EnsimeProjectServer(langServer: EnsimeLanguageServer, implicit val config: EnsimeConfig) extends Actor with LazyLogging {
22+
class EnsimeProjectServer(langServer: EnsimeLanguageServer,
23+
implicit val config: EnsimeConfig,
24+
implicit val ensimeServerConfig:EnsimeServerConfig) extends Actor with LazyLogging {
2325
implicit val timeout: Timeout = Timeout(10 seconds)
2426

2527
val broadcaster = context.actorOf(Broadcaster(), "broadcaster")

languageserver/src/main/scala/langserver/core/Connection.scala

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import com.typesafe.scalalogging.LazyLogging
1717
import langserver.messages._
1818
import langserver.types._
1919
import play.api.libs.json._
20-
20+
import com.dhpcs.jsonrpc.JsonRpcMessage._
2121
/**
2222
* A connection that reads and writes Language Server Protocol messages.
2323
*
@@ -92,9 +92,10 @@ class Connection(inStream: InputStream, outStream: OutputStream)(val commandHand
9292

9393
case Right(message) => message match {
9494
case notification: JsonRpcNotificationMessage =>
95-
Notification.read(notification).fold {
95+
Option(Notification.read(notification)).fold(
9696
logger.error(s"No notification type exists with method=${notification.method}")
97-
}(_.fold({ errors => logger.error(s"Invalid Notification: $errors") },
97+
)(_.fold(
98+
errors => logger.error(s"Invalid Notification: $errors"),
9899
notifySubscribers))
99100

100101
case request: JsonRpcRequestMessage =>
@@ -111,55 +112,67 @@ class Connection(inStream: InputStream, outStream: OutputStream)(val commandHand
111112
case m =>
112113
logger.error(s"Received unknown message: $m")
113114
}
115+
case m => logger.error(s"Received unknown message: $m")
114116
}
115117
}
116118
} while (!streamClosed)
117119
}
118120

119-
private def readJsonRpcMessage(jsonString: String): Either[JsonRpcResponseError, JsonRpcMessage] = {
121+
private def readJsonRpcMessage(jsonString: String): Either[JsonRpcResponseErrorMessage, JsonRpcMessage] = {
120122
logger.debug(s"Received $jsonString")
121123
Try(Json.parse(jsonString)) match {
122124
case Failure(exception) =>
123-
Left(JsonRpcResponseError.parseError(exception))
125+
Left(JsonRpcResponseErrorMessage.parseError(exception,NoCorrelationId))
124126

125127
case Success(json) =>
126128
Json.fromJson[JsonRpcMessage](json).fold({ errors =>
127-
Left(JsonRpcResponseError.invalidRequest(errors))
129+
Left(JsonRpcResponseErrorMessage.invalidRequest(JsError(errors),NoCorrelationId))
128130
}, Right(_))
129131
}
130132
}
131133

132-
private def readCommand(jsonString: String): (Option[Either[String, BigDecimal]], Either[JsonRpcResponseError, ServerCommand]) =
134+
private def readCommand(jsonString: String): (Option[CorrelationId], Either[JsonRpcResponseErrorMessage, ServerCommand]) =
133135
Try(Json.parse(jsonString)) match {
134136
case Failure(exception) =>
135-
None -> Left(JsonRpcResponseError.parseError(exception))
137+
None -> Left(JsonRpcResponseErrorMessage.parseError(exception,NoCorrelationId ))
136138

137139
case Success(json) =>
138140
Json.fromJson[JsonRpcRequestMessage](json).fold(
139-
errors => None -> Left(JsonRpcResponseError.invalidRequest(errors)),
141+
errors => None -> Left(JsonRpcResponseErrorMessage.invalidRequest(JsError(errors),NoCorrelationId)),
140142

141143
jsonRpcRequestMessage =>
142-
ServerCommand.read(jsonRpcRequestMessage)
143-
.fold[(Option[Either[String, BigDecimal]], Either[JsonRpcResponseError, ServerCommand])](
144-
jsonRpcRequestMessage.id -> Left(JsonRpcResponseError.methodNotFound(jsonRpcRequestMessage.method)))(commandJsResult => commandJsResult.fold(
145-
errors => jsonRpcRequestMessage.id -> Left(JsonRpcResponseError.invalidParams(errors)),
146-
command => jsonRpcRequestMessage.id -> Right(command))))
144+
Option(ServerCommand.read(jsonRpcRequestMessage))
145+
.fold[(Option[CorrelationId], Either[JsonRpcResponseErrorMessage, ServerCommand])](
146+
Some(jsonRpcRequestMessage.id) -> Left(JsonRpcResponseErrorMessage.methodNotFound(jsonRpcRequestMessage.method,jsonRpcRequestMessage.id )))(commandJsResult => commandJsResult.fold(
147+
errors => Some(jsonRpcRequestMessage.id) -> Left(JsonRpcResponseErrorMessage.invalidParams(JsError(errors),jsonRpcRequestMessage.id)),
148+
command => Some(jsonRpcRequestMessage.id) -> Right(command))))
147149

148150
}
149151

150-
private def unpackRequest(request: JsonRpcRequestMessage): (Option[Either[String, BigDecimal]], Either[JsonRpcResponseError, ServerCommand]) = {
151-
ServerCommand.read(request)
152-
.fold[(Option[Either[String, BigDecimal]], Either[JsonRpcResponseError, ServerCommand])](
153-
154-
request.id -> Left(
155-
JsonRpcResponseError.methodNotFound(request.method)))(commandJsResult => commandJsResult.fold(
156-
errors => request.id -> Left(JsonRpcResponseError.invalidParams(errors)),
157-
command => request.id -> Right(command)))
152+
private def unpackRequest(request: JsonRpcRequestMessage): (Option[CorrelationId], Either[JsonRpcResponseErrorMessage, ServerCommand]) = {
153+
Option(ServerCommand.read(request))
154+
.fold[(Option[CorrelationId], Either[JsonRpcResponseErrorMessage, ServerCommand])](
155+
Some(request.id) -> Left(JsonRpcResponseErrorMessage.methodNotFound(request.method,request.id )))(
156+
commandJsResult => commandJsResult.fold(errors =>
157+
Some(request.id) -> Left(JsonRpcResponseErrorMessage.invalidParams(JsError(errors),request.id )),
158+
command => Some(request.id) -> Right(command)))
159+
158160
}
159161

160-
private def handleCommand(method: String, id: Either[String, BigDecimal], command: ServerCommand) = {
162+
private def handleCommand(method: String, id: CorrelationId, command: ServerCommand) = {
161163
Future(commandHandler(method, command)).map { result =>
162-
msgWriter.write(ResultResponse.write(Right(result), Some(id)))
164+
logger.info("Result:"+result)
165+
val r = Try{
166+
result match {
167+
case ls:LocationSeq => JsonRpcResponseSuccessMessage(Json.toJson(ls.locs), id) //Special case, new play-json-rcp can not deal with Seq of JsValue
168+
case r => ResultResponse.write(r, id)
169+
}
170+
}
171+
r.recover{case e => logger.error("ResultResponse.write:"+result); e.printStackTrace }
172+
r.foreach{rJson =>
173+
logger.info("Result json:"+r)
174+
msgWriter.write(rJson)
175+
}
163176
}
164177
}
165178
}

languageserver/src/main/scala/langserver/core/LanguageServer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ class LanguageServer(inStream: InputStream, outStream: OutputStream) extends Laz
8484

8585
}
8686

87-
def gotoDefinitionRequest(textDocument: TextDocumentIdentifier, position: Position): Seq[Location] = {
88-
Seq.empty[Location]
87+
def gotoDefinitionRequest(textDocument: TextDocumentIdentifier, position: Position): LocationSeq = {
88+
LocationSeq(Seq.empty[Location])
8989
}
9090

9191
def hoverRequest(textDocument: TextDocumentIdentifier, position: Position): Hover = {
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package langserver.core
22

33
import com.typesafe.scalalogging.LazyLogging
4+
import scala.util.Try
45

56
object Main extends LazyLogging {
67
def main(args: Array[String]): Unit = {
78
logger.info(s"Starting server in ${System.getenv("PWD")}")
89

9-
val server = new LanguageServer(System.in, System.out)
10-
server.start()
10+
val server = Try {
11+
val s = new LanguageServer(System.in, System.out)
12+
s.start()
13+
}
14+
server.recover{case e => {logger.error(e.getMessage); e.printStackTrace} }
1115
}
1216
}

languageserver/src/main/scala/langserver/messages/Commands.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ case class InitializeResult(capabilities: ServerCapabilities) extends ResultResp
142142

143143
case class Shutdown() extends ServerCommand
144144
object Shutdown {
145-
implicit val format: Format[Shutdown] = Format(
145+
implicit val format: Format[Shutdown] = OFormat(
146146
Reads(jsValue => JsSuccess(Shutdown())),
147-
Writes(s => Json.obj()))
147+
OWrites[Shutdown](s => Json.obj()))
148148
}
149149

150150
case class ShutdownResult(dummy: Int) extends ResultResponse
@@ -248,13 +248,18 @@ object Notification extends NotificationCompanion[Notification] {
248248

249249
case class DocumentSymbolResult(params: Seq[SymbolInformation]) extends ResultResponse
250250

251+
case class LocationSeq(locs: Seq[Location]) extends ResultResponse
252+
251253
object ResultResponse extends ResponseCompanion[Any] {
252254
import JsonRpcUtils._
253255

256+
implicit val positionParamsFormat = Json.format[Location]
257+
254258
override val ResponseFormats = Message.MessageFormats(
255259
"initialize" -> Json.format[InitializeResult],
256260
"textDocument/completion" -> Json.format[CompletionList],
257-
"textDocument/definition" -> implicitly[Format[Seq[Location]]],
261+
"textDocument/definition" -> Json.format[LocationSeq],
262+
//"textDocument/definition" -> implicitly[Format[Seq[Location]]], //Runtime exception with latest play-json-rcp
258263
"textDocument/hover" -> Json.format[Hover],
259264
"textDocument/documentSymbol" -> valueFormat(DocumentSymbolResult)(_.params),
260265
"shutdown" -> Json.format[ShutdownResult])

languageserver/src/main/scala/langserver/utils/JsonRpcUtils.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ object JsonRpcUtils {
3434
* )
3535
* }}}
3636
*/
37-
def valueFormat[A, B: Format](apply: B => A)(unapply: A => B): Format[A] = new Format[A] {
37+
def valueFormat[A, B: Format](apply: B => A)(unapply: A => B): OFormat[A] = new OFormat[A] {
3838
override def reads(json: JsValue) = Reads.of[B].reads(json).map(apply(_))
39-
override def writes(o: A) = Writes.of[B].writes(unapply(o))
39+
override def writes(o: A) = Writes.of[B].writes(unapply(o)).as[JsObject]
4040
}
4141
}

0 commit comments

Comments
 (0)