From 840bc9a508e383257b90aa9d02c70b08d018d895 Mon Sep 17 00:00:00 2001 From: "A. G" Date: Sun, 15 Apr 2018 16:45:32 +0100 Subject: [PATCH] LSP - in progress --- src/main/scala/com/neowit/apex/Runner.scala | 4 +- .../apex/lsp/ApexLanguageServerBase.scala | 35 ++++++-- .../com/neowit/response/ResponseWriter.scala | 7 +- .../response/protocols/lsp/AppVersion.scala | 43 ++++++++++ .../protocols/lsp/ResponseWriterLsp.scala | 80 +++++++++++++++++++ 5 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 src/main/scala/com/neowit/response/protocols/lsp/AppVersion.scala create mode 100644 src/main/scala/com/neowit/response/protocols/lsp/ResponseWriterLsp.scala diff --git a/src/main/scala/com/neowit/apex/Runner.scala b/src/main/scala/com/neowit/apex/Runner.scala index db04ce34..6e263ffe 100644 --- a/src/main/scala/com/neowit/apex/Runner.scala +++ b/src/main/scala/com/neowit/apex/Runner.scala @@ -62,7 +62,7 @@ class Executor extends Logging { } catch { case ex: InvalidCommandLineException => if (null != ex.getMessage) { - basicConfig.getResponseWriter.send(ex.getMessage) + basicConfig.getResponseWriter.sendError("Invalid parameter: ", ex) logger.error(ex.getMessage) } basicConfig.help() @@ -78,7 +78,7 @@ class Executor extends Logging { System.out.println(stackTraceStr) */ if (ex.message.nonEmpty) { - basicConfig.getResponseWriter.send(ex.message) + basicConfig.getResponseWriter.sendError("", ex) logger.error(ex.message) } help(ex.help) diff --git a/src/main/scala/com/neowit/apex/lsp/ApexLanguageServerBase.scala b/src/main/scala/com/neowit/apex/lsp/ApexLanguageServerBase.scala index a3469eb8..d771d245 100644 --- a/src/main/scala/com/neowit/apex/lsp/ApexLanguageServerBase.scala +++ b/src/main/scala/com/neowit/apex/lsp/ApexLanguageServerBase.scala @@ -27,6 +27,7 @@ import com.neowit.apex.{Executor, ProjectsCache, Session} import com.neowit.apexscanner.Project import com.neowit.apexscanner.server.LanguageServerDefault import com.neowit.apexscanner.server.protocol.messages._ +import com.neowit.response.protocols.lsp.ResponseWriterLsp import com.neowit.utils.{BasicConfig, FileUtils} import scala.concurrent.{ExecutionContext, Future} @@ -132,20 +133,40 @@ class ApexLanguageServerBase(inputStream: InputStream, outputStream: OutputStrea } } - override def executeCommand(messageId: Int, params: MessageParams.ExecuteCommandParams, projectOpt: Option[Project]): Future[Either[ResponseError, ResponseMessage]] = { - logger.debug("execute command: " + params.command + " with arguments: " + params.arguments + " in project: " + projectOpt.map(_.path).getOrElse("")) + override def executeCommand(messageId: Int, params: MessageParams.ExecuteCommandParams, projectOpt: Option[Project]): Future[ResponseMessage] = { + val logMsg = "execute command: " + params.command + " with arguments: " + params.arguments + " in project: " + projectOpt.map(_.path).getOrElse("") + logger.debug(logMsg) + sendLogMessageNotification(MessageType.Log, logMsg) + val commandLineArgsMap: Map[String, String] = messageParamsToMap(params, projectOpt) + val responseWriter = new ResponseWriterLsp(messageId, this) val runner = new Executor() - + runner.basicConfig.setResponseWriter(responseWriter) runner.execute(commandLineArgsMap) - super.executeCommand(messageId, params, projectOpt) + responseWriter.result() match { + case Some(msg) => + Future.successful(msg) + case None => + Future.successful(ResponseMessage(messageId, result = None, error = None)) + } } - override def executeCommand(messageId: Int, command: String): Future[Either[ResponseError, ResponseMessage]] = { - logger.debug("execute command: " + command + " without arguments") + override def executeCommand(messageId: Int, command: String): Future[ResponseMessage] = { + val logMsg = "execute command: " + command + " without arguments" + logger.debug(logMsg) + sendLogMessageNotification(MessageType.Log, logMsg) + val commandLineArgs: Array[String] = Array("--action=" + command) + val responseWriter = new ResponseWriterLsp(messageId, this) + val runner = new Executor() + runner.basicConfig.setResponseWriter(responseWriter) runner.execute(commandLineArgs) - Future.successful(Right(ResponseMessage(messageId, result = None, error = None))) + responseWriter.result() match { + case Some(msg) => + Future.successful(msg) + case None => + Future.successful(ResponseMessage(messageId, result = None, error = None)) + } } private def messageParamsToMap(params: MessageParams.ExecuteCommandParams, projectOpt: Option[Project]): Map[String, String] = { diff --git a/src/main/scala/com/neowit/response/ResponseWriter.scala b/src/main/scala/com/neowit/response/ResponseWriter.scala index 2c799c7e..4f98cfe8 100644 --- a/src/main/scala/com/neowit/response/ResponseWriter.scala +++ b/src/main/scala/com/neowit/response/ResponseWriter.scala @@ -22,7 +22,7 @@ package com.neowit.response import java.io.File import com.neowit.apex.actions.ActionResult -import com.neowit.utils.{FileUtils, JsonSupport, Logging} +import com.neowit.utils.{FileUtils, Logging} object ResponseWriter { @@ -74,10 +74,13 @@ object ResponseWriter { } -trait ResponseWriter extends Logging with JsonSupport { +trait ResponseWriter extends Logging { def sendResponse(result: ActionResult): Unit def send(msg: Message): Message + def sendError(msg: String, ex: Exception): Unit = { + send(msg + " " + ex.getMessage) + } def send(msg: String): Unit def send(msg: RESULT): Unit diff --git a/src/main/scala/com/neowit/response/protocols/lsp/AppVersion.scala b/src/main/scala/com/neowit/response/protocols/lsp/AppVersion.scala new file mode 100644 index 00000000..51939bf5 --- /dev/null +++ b/src/main/scala/com/neowit/response/protocols/lsp/AppVersion.scala @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2018 Andrey Gavrikov. + * this file is part of tooling-force.com application + * https://github.com/neowit/tooling-force.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.neowit.response.protocols.lsp + +import com.neowit.apex.actions.ActionSuccess +import com.neowit.response._ + +class AppVersion(writer: ResponseWriterLsp) extends LspProtocol[AppVersionResult] { + def send(result: AppVersionResult): Unit = { + val msg = + result.appName + " - version: " + result.appVersion + + "; SFDC API Version: " + result.sfdcApiVersion + + "; java: " + result.javaVersion + + "; OS: " + result.os + + writer.sendResponse(ActionSuccess(msg)) + + val mb = 1024*1024 + val runtime = Runtime.getRuntime + + writer.debug( s"Used Memory: ${(runtime.totalMemory - runtime.freeMemory) / mb} MB") + writer.debug( s"Free Memory: ${runtime.freeMemory / mb} MB") + writer.debug( s"Total Memory: ${runtime.totalMemory / mb} MB") + writer.debug( s"Max Memory: ${runtime.maxMemory / mb} MB") + } +} diff --git a/src/main/scala/com/neowit/response/protocols/lsp/ResponseWriterLsp.scala b/src/main/scala/com/neowit/response/protocols/lsp/ResponseWriterLsp.scala new file mode 100644 index 00000000..40ce8327 --- /dev/null +++ b/src/main/scala/com/neowit/response/protocols/lsp/ResponseWriterLsp.scala @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018 Andrey Gavrikov. + * this file is part of tooling-force.com application + * https://github.com/neowit/tooling-force.com + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.neowit.response.protocols.lsp + +import java.io.{PrintWriter, StringWriter} + +import com.neowit.apex.actions.{ActionFailure, ActionResult, ActionSuccess} +import com.neowit.apexscanner.server.protocol.LanguageServer +import com.neowit.apexscanner.server.protocol.messages._ +import com.neowit.response +import com.neowit.response.{BaseResult, RESULT, ResponseWriter} +import com.neowit.utils.Logging + +import io.circe.syntax._ +/** + * Created by Andrey Gavrikov + */ +trait LspProtocol[A <: BaseResult] { + def send(result: A): Unit +} +class ResponseWriterLsp(messageId: Int, server: LanguageServer) extends ResponseWriter with Logging with MessageJsonSupport { + private var _responseMessage: Option[ResponseMessage] = None + + override def sendResponse(result: ActionResult): Unit = { + // TODO - implement proper handling of messages and resultOpt + + result match { + case ActionSuccess(messages, resultOpt) => + val totalMessage = messages.mkString("; ") + _responseMessage = Option(ResponseMessage(messageId, result = Option(totalMessage.asJson), error = None)) + case ActionFailure(messages, resultOpt) => + val totalMessage = messages.mkString("; ") + val error = ResponseError(0, totalMessage, messageId = Option(messageId)) + _responseMessage = Option(ResponseMessage(messageId, result = None, Option(error))) + } + } + + override def send(msg: response.Message): response.Message = throw new UnsupportedOperationException + + override def send(msg: String): Unit = throw new UnsupportedOperationException + + override def send(msg: RESULT): Unit = throw new UnsupportedOperationException + + override def sendError(msg: String, ex: Exception): Unit = { + val sw = new StringWriter + ex.printStackTrace(new PrintWriter(sw)) + val stackTraceStr = sw.toString + // dump exception information to log + logger.error(ex.getMessage) + logger.error(stackTraceStr) + + val err = ResponseError(0, msg + ex.getMessage + "\n" + stackTraceStr) + _responseMessage = Option(ResponseMessage(messageId, result = None, error = Option(err))) + } + + override def close(): Unit = () + + def debug(msg: String): Unit = { + server.sendLogMessageNotification(MessageType.Log, msg) + } + + def result(): Option[ResponseMessage] = _responseMessage +}