From 795cfeb28721d6412822aec3995227edca6cd30a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20P=C3=A1ll=20Geirsson?= Date: Thu, 23 Nov 2017 09:48:44 +0100 Subject: [PATCH 1/4] Use stable version number outside of CI. It grows very tiring to manually update extension.js on every new commit. --- build.sbt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.sbt b/build.sbt index db026ebb026..df46543c6e7 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,9 @@ inThisBuild( List( + version ~= { old => + if (sys.env.contains("CI")) old + else "0.1-SNAPSHOT" // to avoid manually updating extension.js + }, scalaVersion := "2.12.3", organization := "org.scalameta", licenses := Seq( From c741d3737e88299f6c12e4db6d6de30e0330e2ba Mon Sep 17 00:00:00 2001 From: Alexey Alekhin Date: Thu, 23 Nov 2017 22:21:31 +0100 Subject: [PATCH 2/4] Added a workaround to handle parameter-less shutdown command --- .../langserver/core/LanguageServer.scala | 6 ++--- .../scala/langserver/messages/Commands.scala | 24 ++++++++++++------- .../ScalametaLanguageServer.scala | 3 ++- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/languageserver/src/main/scala/langserver/core/LanguageServer.scala b/languageserver/src/main/scala/langserver/core/LanguageServer.scala index ced6e3465eb..6f4ecb4ab3e 100644 --- a/languageserver/src/main/scala/langserver/core/LanguageServer.scala +++ b/languageserver/src/main/scala/langserver/core/LanguageServer.scala @@ -30,7 +30,7 @@ class LanguageServer(inStream: InputStream, outStream: OutputStream) extends Laz case (_, Shutdown()) => shutdown() - ShutdownResult(0) // the value is a dummy, because Play Json needs to serialize something + ShutdownResult() case c => logger.error(s"Unknown command $c") sys.error("Unknown command") @@ -82,9 +82,7 @@ class LanguageServer(inStream: InputStream, outStream: OutputStream) extends Laz CompletionList(isIncomplete = false, Nil) } - def shutdown(): Unit = { - - } + def shutdown(): Unit = {} def gotoDefinitionRequest(textDocument: TextDocumentIdentifier, position: Position): DefinitionResult = { DefinitionResult(Seq.empty[Location]) diff --git a/languageserver/src/main/scala/langserver/messages/Commands.scala b/languageserver/src/main/scala/langserver/messages/Commands.scala index 13a47196606..6e65f427e7d 100644 --- a/languageserver/src/main/scala/langserver/messages/Commands.scala +++ b/languageserver/src/main/scala/langserver/messages/Commands.scala @@ -141,13 +141,13 @@ object CompletionList { case class InitializeResult(capabilities: ServerCapabilities) extends ResultResponse case class Shutdown() extends ServerCommand -object Shutdown { - implicit val format: Format[Shutdown] = OFormat( - Reads(jsValue => JsSuccess(Shutdown())), - OWrites[Shutdown](s => Json.obj())) -} -case class ShutdownResult(dummy: Int) extends ResultResponse +case class ShutdownResult() extends ResultResponse +object ShutdownResult { + implicit val format: Format[ShutdownResult] = OFormat( + Reads(jsValue => JsSuccess(ShutdownResult())), + OWrites[ShutdownResult](s => Json.obj())) +} case class ShowMessageRequestParams( /** @@ -193,13 +193,20 @@ object ServerCommand extends CommandCompanion[ServerCommand] { override val CommandFormats = Message.MessageFormats( "initialize" -> Json.format[InitializeParams], - "shutdown" -> Shutdown.format, "textDocument/completion" -> valueFormat(TextDocumentCompletionRequest)(_.params), "textDocument/definition" -> valueFormat(TextDocumentDefinitionRequest)(_.params), "textDocument/hover" -> valueFormat(TextDocumentHoverRequest)(_.params), "textDocument/documentSymbol" -> Json.format[DocumentSymbolParams], "textDocument/formatting" -> valueFormat(TextDocumentFormattingRequest)(_.params) ) + + // NOTE: this is a workaround to read `shutdown` request which doesn't have parameters (scala-json-rpc requires parameters for all requests) + override def read(jsonRpcRequestMessage: JsonRpcRequestMessage): JsResult[_ <: ServerCommand] = { + jsonRpcRequestMessage.method match { + case "shutdown" => JsSuccess(Shutdown()) + case _ => super.read(jsonRpcRequestMessage) + } + } } object ClientCommand extends CommandCompanion[ClientCommand] { @@ -275,5 +282,6 @@ object ResultResponse extends ResponseCompanion[Any] { "textDocument/hover" -> Json.format[Hover], "textDocument/documentSymbol" -> valueFormat(DocumentSymbolResult)(_.params), "textDocument/formatting" -> valueFormat(DocumentFormattingResult)(_.params), - "shutdown" -> Json.format[ShutdownResult]) + "shutdown" -> ShutdownResult.format + ) } diff --git a/metaserver/src/main/scala/scala/meta/languageserver/ScalametaLanguageServer.scala b/metaserver/src/main/scala/scala/meta/languageserver/ScalametaLanguageServer.scala index c64157b29bb..be330568130 100644 --- a/metaserver/src/main/scala/scala/meta/languageserver/ScalametaLanguageServer.scala +++ b/metaserver/src/main/scala/scala/meta/languageserver/ScalametaLanguageServer.scala @@ -282,7 +282,8 @@ class ScalametaLanguageServer( } catch { case NonFatal(e) => onError(e) - ShutdownResult(-1) + // FIXME: server shouldn't send shutdown response if there was no shutdown request + ShutdownResult() } } From af8e6d1fa3b1878245ef472a189cc876178e60dd Mon Sep 17 00:00:00 2001 From: Alexey Alekhin Date: Thu, 23 Nov 2017 22:22:13 +0100 Subject: [PATCH 3/4] Same workaround for handling exit notification --- .../main/scala/langserver/core/LanguageServer.scala | 7 +++++++ .../src/main/scala/langserver/messages/Commands.scala | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/languageserver/src/main/scala/langserver/core/LanguageServer.scala b/languageserver/src/main/scala/langserver/core/LanguageServer.scala index 6f4ecb4ab3e..bc3d60881c0 100644 --- a/languageserver/src/main/scala/langserver/core/LanguageServer.scala +++ b/languageserver/src/main/scala/langserver/core/LanguageServer.scala @@ -40,6 +40,7 @@ class LanguageServer(inStream: InputStream, outStream: OutputStream) extends Laz protected val documentManager = new TextDocumentManager(connection) connection.notificationHandlers += { + case Exit() => onExit() case DidOpenTextDocumentParams(td) => onOpenTextDocument(td) case DidChangeTextDocumentParams(td, changes) => onChangeTextDocument(td, changes) case DidSaveTextDocumentParams(td) => onSaveTextDocument(td) @@ -52,6 +53,12 @@ class LanguageServer(inStream: InputStream, outStream: OutputStream) extends Laz connection.start() } + def onExit(): Unit = { + logger.debug("exit") + // TODO: should exit with success code 0 if the shutdown request has been received before; otherwise with error code 1 + sys.exit(0) + } + def onOpenTextDocument(td: TextDocumentItem) = { logger.debug(s"openTextDocuemnt $td") } diff --git a/languageserver/src/main/scala/langserver/messages/Commands.scala b/languageserver/src/main/scala/langserver/messages/Commands.scala index 6e65f427e7d..1ce543281ef 100644 --- a/languageserver/src/main/scala/langserver/messages/Commands.scala +++ b/languageserver/src/main/scala/langserver/messages/Commands.scala @@ -224,7 +224,8 @@ case class PublishDiagnostics(uri: String, diagnostics: Seq[Diagnostic]) extends // from client to server -case class ExitNotification() extends Notification +case class Exit() extends Notification + case class DidOpenTextDocumentParams(textDocument: TextDocumentItem) extends Notification case class DidChangeTextDocumentParams( textDocument: VersionedTextDocumentIdentifier, @@ -266,6 +267,14 @@ object Notification extends NotificationCompanion[Notification] { "initialized" -> Initialized.format, "$/cancelRequest" -> Json.format[CancelRequest] ) + + // NOTE: this is a workaround to read `exit` notification which doesn't have parameters (scala-json-rpc requires parameters for all notifications) + override def read(jsonRpcNotificationMessage: JsonRpcNotificationMessage): JsResult[_ <: Notification] = { + jsonRpcNotificationMessage.method match { + case "exit" => JsSuccess(Exit()) + case _ => super.read(jsonRpcNotificationMessage) + } + } } case class DocumentSymbolResult(params: Seq[SymbolInformation]) extends ResultResponse From f3d9342b4a408f9fd03ae85e14c26edc0e22b773 Mon Sep 17 00:00:00 2001 From: Alexey Alekhin Date: Thu, 23 Nov 2017 22:46:10 +0100 Subject: [PATCH 4/4] Removed try-catch blocks in request hadnlers --- .../ScalametaLanguageServer.scala | 72 ++++++++----------- 1 file changed, 29 insertions(+), 43 deletions(-) diff --git a/metaserver/src/main/scala/scala/meta/languageserver/ScalametaLanguageServer.scala b/metaserver/src/main/scala/scala/meta/languageserver/ScalametaLanguageServer.scala index be330568130..884f78e821c 100644 --- a/metaserver/src/main/scala/scala/meta/languageserver/ScalametaLanguageServer.scala +++ b/metaserver/src/main/scala/scala/meta/languageserver/ScalametaLanguageServer.scala @@ -155,27 +155,20 @@ class ScalametaLanguageServer( td: TextDocumentIdentifier, options: FormattingOptions ): List[TextEdit] = { - try { - val path = Uri.toPath(td.uri).get - val contents = buffers.read(path) - val fullDocumentRange = Range( - start = Position(0, 0), - end = Position(Int.MaxValue, Int.MaxValue) - ) - val config = cwd.resolve(".scalafmt.conf") - if (Files.isRegularFile(config.toNIO)) { - val formattedContent = - scalafmt.format(contents, path.toString(), config) - List(TextEdit(fullDocumentRange, formattedContent)) - } else { - connection.showMessage(MessageType.Info, s"Missing $config") - Nil - } - } catch { - case NonFatal(e) => - connection.showMessage(MessageType.Error, e.getMessage) - logger.error(e.getMessage, e) - Nil + val path = Uri.toPath(td.uri).get + val contents = buffers.read(path) + val fullDocumentRange = Range( + start = Position(0, 0), + end = Position(Int.MaxValue, Int.MaxValue) + ) + val config = cwd.resolve(".scalafmt.conf") + if (Files.isRegularFile(config.toNIO)) { + val formattedContent = + scalafmt.format(contents, path.toString(), config) + List(TextEdit(fullDocumentRange, formattedContent)) + } else { + connection.showMessage(MessageType.Info, s"Missing $config") + Nil } } @@ -263,28 +256,21 @@ class ScalametaLanguageServer( td: TextDocumentIdentifier, position: Position ): ResultResponse = { - try { - val completions = compiler.autocomplete( - Uri.toPath(td.uri).get, - position.line, - position.character - ) - CompletionList( - isIncomplete = false, - items = completions.map { - case (signature, name) => - CompletionItem( - label = name, - detail = Some(signature) - ) - } - ) - } catch { - case NonFatal(e) => - onError(e) - // FIXME: server shouldn't send shutdown response if there was no shutdown request - ShutdownResult() - } + val completions = compiler.autocomplete( + Uri.toPath(td.uri).get, + position.line, + position.character + ) + CompletionList( + isIncomplete = false, + items = completions.map { + case (signature, name) => + CompletionItem( + label = name, + detail = Some(signature) + ) + } + ) } override def hoverRequest(