diff --git a/libsolidity/lsp/LanguageServer.cpp b/libsolidity/lsp/LanguageServer.cpp index f704eafee..7e38a37d7 100644 --- a/libsolidity/lsp/LanguageServer.cpp +++ b/libsolidity/lsp/LanguageServer.cpp @@ -189,11 +189,11 @@ void LanguageServer::changeConfiguration(Json::Value const& _settings) if (auto traceLogFileNode = _settings["trace-log-file"]) { if (traceLogFileNode.isString()) - m_client.setTraceLogFile(boost::filesystem::path(_settings["trace-log-file"].asString())); - if (traceLogFileNode.isNull()) + m_client.setTraceLogFile(boost::filesystem::path(traceLogFileNode.asString())); + else if (traceLogFileNode.isNull()) m_client.setTraceLogFile(nullopt); else - lspAssert(false, ErrorCode::InvalidParams, "Invalid trace-log-file value. Must be a path to a file."); + lspAssert(false, ErrorCode::InvalidParams, "Invalid trace-log-file value. Must be a path to a file. "s + std::to_string(traceLogFileNode.type())); } m_settingsObject = _settings; diff --git a/libsolidity/lsp/Transport.cpp b/libsolidity/lsp/Transport.cpp index 608127d55..3cabec0f5 100644 --- a/libsolidity/lsp/Transport.cpp +++ b/libsolidity/lsp/Transport.cpp @@ -42,6 +42,12 @@ using namespace std; using namespace solidity::lsp; // {{{ Transport +ofstream Transport::traceLogFileStream() const +{ + lspAssert(m_traceLogFilePath, ErrorCode::InternalError, ""); + return ofstream(m_traceLogFilePath.value().generic_string(), ios::app); +} + optional Transport::receive() { auto const headers = parseHeaders(); @@ -59,21 +65,21 @@ optional Transport::receive() string const data = readBytes(stoul(headers->at("content-length"))); - if (m_traceLogFilePath) - { - ofstream traceLogger(m_traceLogFilePath.value().generic_string(), ios::app); - traceLogger << "Received: " << data << endl; - } - Json::Value jsonMessage; string jsonParsingErrors; solidity::util::jsonParseStrict(data, jsonMessage, &jsonParsingErrors); if (!jsonParsingErrors.empty() || !jsonMessage || !jsonMessage.isObject()) { + if (m_traceLogFilePath) + traceLogFileStream() << "{\"Received\": " << util::jsonPrettyPrint(jsonMessage) << "}," << endl; + error({}, ErrorCode::ParseError, "Could not parse RPC JSON payload. " + jsonParsingErrors); return nullopt; } + if (m_traceLogFilePath) + traceLogFileStream() << "{\"Received\": " << util::jsonPrettyPrint(jsonMessage) << "}," << endl; + return {std::move(jsonMessage)}; } @@ -150,10 +156,7 @@ void Transport::send(Json::Value _json, MessageID _id) string const jsonString = solidity::util::jsonCompactPrint(_json); if (m_traceLogFilePath) - { - ofstream traceLogger(m_traceLogFilePath.value().generic_string(), ios::app); - traceLogger << "Sending: " << jsonString << endl; - } + traceLogFileStream() << "{\"Sending\": " << jsonString << "}," << endl; writeBytes(fmt::format("Content-Length: {}\r\n\r\n", jsonString.size())); writeBytes(jsonString); diff --git a/libsolidity/lsp/Transport.h b/libsolidity/lsp/Transport.h index d1df026ed..86ba74c75 100644 --- a/libsolidity/lsp/Transport.h +++ b/libsolidity/lsp/Transport.h @@ -109,6 +109,8 @@ public: /// to be written to if trace value is set to verbose. void setTraceLogFile(std::optional _pathToLogfile); + std::ofstream traceLogFileStream() const; + private: TraceValue m_logTrace = TraceValue::Off; std::optional m_traceLogFilePath; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e215405a1..3a12e0790 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -913,6 +913,8 @@ void CommandLineInterface::handleAst() void CommandLineInterface::serveLSP() { lsp::StdioTransport transport; + transport.setTraceLogFile(m_options.languageServer.traceLogFile); + if (!lsp::LanguageServer{transport}.run()) solThrow(CommandLineExecutionError, "LSP terminated abnormally."); } diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 2c765d0e3..f7e89b13c 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -61,6 +61,7 @@ static string const g_strLicense = "license"; static string const g_strLibraries = "libraries"; static string const g_strLink = "link"; static string const g_strLSP = "lsp"; +static string const g_strLSPTrace = "lsp-trace"; static string const g_strMachine = "machine"; static string const g_strMetadataHash = "metadata-hash"; static string const g_strMetadataLiteral = "metadata-literal"; @@ -649,6 +650,16 @@ General Information)").c_str(), ; desc.add(alternativeInputModes); + po::options_description lspModeOptions("LSP Mode Options"); + lspModeOptions.add_options() + ( + g_strLSPTrace.c_str(), + po::value()->value_name(""), + "Enables trace I/O logging to a given file." + ) + ; + desc.add(lspModeOptions); + po::options_description assemblyModeOptions("Assembly Mode Options"); assemblyModeOptions.add_options() ( @@ -942,7 +953,12 @@ void CommandLineParser::processArgs() ); if (m_options.input.mode == InputMode::LanguageServer) + { + if (m_args.count("lsp-trace")) + m_options.languageServer.traceLogFile = boost::filesystem::path(m_args.at("lsp-trace").as()); + return; + } checkMutuallyExclusive({g_strColor, g_strNoColor}); diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 108a16cd2..50ba66e52 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -227,6 +227,11 @@ struct CommandLineOptions std::optional yulSteps; } optimizer; + struct + { + std::optional traceLogFile = std::nullopt; + } languageServer; + struct { bool initialize = false;