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

Commit 811f9c4

Browse files
authored
Merge pull request #54 from fede0664/master
Compiles and run on Scala 2.12, latest version of Ensime (2.0.0-M4). Added settings to change server log level from VSCode
2 parents bc38084 + bbd78b7 commit 811f9c4

File tree

12 files changed

+140
-61
lines changed

12 files changed

+140
-61
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ If VSCode is running behind a proxy add the following standard VSCode proxy sett
6868

6969
This setting is translated as Coursier's vm arguments: -Dhttp.proxyHost=host -Dhttps.proxyHost=host -Dhttp.proxyPort=port -Dhttps.proxyPort=port.
7070

71+
Log level settting:
72+
{
73+
"scalaLanguageServer.logLevel" : "DEBUG"
74+
}
75+
76+
This setting is passed to the Language Server affecting the log leven on the server, possible values "DEBUG", "ERROR", "INFO", "WARN"
77+
78+
7179
# Publish
7280

7381
```

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/resources/logback.groovy

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
def WORKSPACE_LOCATION = System.getProperty("vscode.workspace")
23

34
if (WORKSPACE_LOCATION == null)
@@ -11,9 +12,16 @@ appender("FILE", FileAppender) {
1112
}
1213
}
1314

15+
def logLevel = System.getProperty("vscode.logLevel")
16+
def LOG_LEVEL = DEBUG
17+
if(logLevel == "ERROR") LOG_LEVEL = ERROR
18+
else if(logLevel == "INFO") LOG_LEVEL = INFO
19+
else if(logLevel == "WARN") LOG_LEVEL = WARN
20+
else LOG_LEVEL = DEBUG
21+
1422
root(INFO, ["FILE"])
1523
logger("slick", ERROR, ["FILE"])
16-
logger("org.github.dragos.vscode", DEBUG, ["FILE"])
17-
logger("langserver.core", INFO, ["FILE"])
24+
logger("org.github.dragos.vscode", LOG_LEVEL, ["FILE"])
25+
logger("langserver.core", LOG_LEVEL, ["FILE"])
1826
logger("scala.tools.nsc", ERROR, ["FILE"])
1927
logger("com.zaxxer.hikari", ERROR, ["FILE"])

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/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: 36 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 - Message: $message"),
9899
notifySubscribers))
99100

100101
case request: JsonRpcRequestMessage =>
@@ -111,55 +112,66 @@ 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+
val r = Try{
165+
result match {
166+
case ls: LocationSeq => JsonRpcResponseSuccessMessage(Json.toJson(ls.locs), id) //Special case, new play-json-rcp can not deal with Seq of JsValue
167+
case ds: DocumentSymbolResult => JsonRpcResponseSuccessMessage(Json.toJson(ds.params), 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+
msgWriter.write(rJson)
174+
}
163175
}
164176
}
165177
}

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
}

0 commit comments

Comments
 (0)